+ MCI support for focusArgs, e.g. 'focusTextStyle'

+ TextView initial support for fillChar and justify
+ Add color support to string_util.js::pad()
* Better color handling in Views
This commit is contained in:
Bryan Ashby 2014-11-03 16:49:15 -07:00
parent 4234e03008
commit 6c841105ab
13 changed files with 126 additions and 162 deletions

View File

@ -433,6 +433,7 @@ function display(art, options, cb) {
bg : parser.bgColor,
flags : parser.flags,
};
mci[mapItem].focusArgs = args;
} else {
mci[mapItem] = {
args : args,

View File

@ -9,8 +9,10 @@ var assert = require('assert');
exports.ButtonView = ButtonView;
function ButtonView(client, options) {
console.log(options);
options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true);
options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true);
options.justify = miscUtil.valueWithDefault(options.justify, 'center');
TextView.call(this, client, options);
}
@ -21,6 +23,7 @@ ButtonView.prototype.onKeyPress = function(key, isSpecial) {
ButtonView.super_.prototype.onKeyPress.call(this, key, isSpecial);
// allow spacebar to 'click' buttons
// :TODO: need to check configurable mapping here
if(' ' === key) {
this.emit('action', 'accept');
}

View File

@ -16,7 +16,8 @@ function EditTextView(client, options) {
TextView.call(this, client, options);
this.clientBackspace = function() {
this.client.term.write('\b \b');
this.client.term.write(
'\b' + this.getANSIColor(this.getColor()) + this.fillChar + '\b' + this.getANSIColor(this.getFocusColor()));
};
}
@ -29,7 +30,7 @@ EditTextView.prototype.onKeyPress = function(key, isSpecial) {
assert(1 === key.length);
console.log('key: ' + key);
// :TODO: how to handle justify left/center?
if(this.text.length < this.options.maxLength) {
key = strUtil.stylizeString(key, this.textStyle);

View File

@ -1,64 +0,0 @@
"use strict";
var assert = require('assert');
var miscUtil = require('./misc_util.js');
exports.LineEditor = LineEditor;
var STANDARD_KEYSET = {
refresh : [ 12 ],
backspace : [ 8, 127 ],
backword : [ 23 ],
enter : [ 10 ],
exit : [ 27 ],
};
// :TODO: Rename to TextEdit
// :TODO: TextEdit should be single or multi line
function LineEditor(client, options) {
var self = this;
self.client = client;
self.valueText = '';
if(typeof options !== 'undefined') {
self.options.keyset = miscUtil.valueWithDefault(options.keyset, STANDARD_KEYSET);
} else {
self.options = {
keyset : STANDARD_KEYSET,
};
}
this.client.on('data', function onData(data) {
assert(1 === data.length);
self.onCh(data);
});
};
LineEditor.prototype.isKey = function(setName, ch) {
return this.options.keyset[setName].indexOf(ch) > -1;
}
LineEditor.prototype.onCh = function(ch) {
if(this.isKey('refresh', ch)) {
} else if(this.isKey('backspace', ch)) {
} else if(this.isKey('backword', ch)) {
} else if(this.isKey('enter', ch)) {
} else if(this.isKey('exit', ch)) {
} else {
// :TODO: filter out chars
// :TODO: check max width
this.valueText += ch;
this.client.term.write(ch);
}
};

View File

@ -26,13 +26,6 @@ MCIViewFactory.prototype.getPredefinedViewLabel = function(name) {
return label;
};
// :TODO: probably do something like this and generalize all of this:
/*
var MCI_ARG_MAP = {
'ET' : { 0 : 'maxLength', 1 : 'textStyle' }
};
*/
MCIViewFactory.prototype.createFromMCI = function(mci) {
assert(mci.code);
assert(mci.id > 0);
@ -46,7 +39,6 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
position : { x : mci.position[0], y : mci.position[1] },
};
// :TODO: move this stuff out of the switch to their own methods/objects
function setOption(pos, name) {
if(mci.args.length > pos && mci.args[pos].length > 0) {
options[name] = mci.args[pos];
@ -55,19 +47,19 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
return false;
}
function setFocusOption(pos, name) {
if(mci.focusArgs && mci.focusArgs.length > pos && mci.focusArgs[pos].length > 0) {
options[name] = mci.focusArgs[pos];
}
return false;
}
switch(mci.code) {
case 'TL' :
// :TODO: convert to setOption()
if(mci.args.length > 0) {
options.textStyle = mci.args[0];
}
if(mci.args.length > 1) {
options.justify = mci.args[1];
}
if(mci.args.length > 2) {
options.maxLength = mci.args[2];
setOption(0, 'textStyle');
setOption(1, 'justify');
if(setOption(2, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength };
}
@ -76,29 +68,26 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
case 'ET' :
if(setOption(0, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength };
}
setOption(1, 'textStyle');
setFocusOption(0, 'focusTextStyle');
view = new EditTextView(this.client, options);
break;
case 'PL' :
// :TODO: convert to setOption()
if(mci.args.length > 0) {
options.text = this.getPredefinedViewLabel(mci.args[0]);
if(options.text) {
if(mci.args.length > 1) {
options.textStyle = mci.args[1];
}
setOption(1, 'textStyle');
setOption(2, 'justify');
if(mci.args.length > 2) {
options.justify = mci.args[2];
}
if(mci.args.length > 3) {
options.maxLength = mci.args[3];
if(setOption(3, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength };
}
@ -107,13 +96,16 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
}
break;
case 'BV' :
// :TODO: convert to setOption()
case 'BN' :
if(mci.args.length > 0) {
options.text = mci.args[0];
options.dimens = { width : options.text.length };
options.dimens = { width : parseInt(mci.args[0], 10) };
}
setOption(1, 'textStyle');
setOption(2, 'justify');
setFocusOption(0, 'focusTextStyle');
view = new ButtonView(this.client, options);
break;
@ -122,6 +114,8 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
setOption(1, 'justify');
setOption(2, 'textStyle');
setFocusOption(0, 'focusTextStyle');
view = new VerticalMenuView(this.client, options);
break;
}

View File

@ -34,10 +34,43 @@ function MenuView(client, options) {
this.fillChar = miscUtil.valueWithDefault(this.options.fillChar, ' ').substr(0, 1);
this.justify = this.options.justify || 'none';
this.moveSelection = function(fromIndex, toIndex) {
assert(!self.xPositionCacheExpired);
assert(fromIndex >= 0 && fromIndex <= self.items.length);
assert(toIndex >= 0 && toIndex <= self.items.length);
self.items[fromIndex].focused = false;
self.drawItem(fromIndex);
self.items[toIndex].focused = true;
self.focusedItemIndex = toIndex;
self.drawItem(toIndex);
};
this.cachePositions = function() {
// :TODO: implement me!
};
this.drawItem = function(index) {
// :TODO: implement me!
};
}
util.inherits(MenuView, View);
MenuView.prototype.redraw = function() {
MenuView.super_.prototype.redraw.call(this);
this.cachePositions();
var count = this.items.length;
for(var i = 0; i < count; ++i) {
this.items[i].focused = this.focusedItemIndex === i;
this.drawItem(i);
}
};
MenuView.prototype.setItems = function(items) {
var self = this;
if(items) {

View File

@ -112,17 +112,19 @@ function stylizeString(s, style) {
}
// Based on http://www.webtoolkit.info/
function pad(s, len, padChar, dir) {
function pad(s, len, padChar, dir, stringColor, padColor) {
len = miscUtil.valueWithDefault(len, 0);
padChar = miscUtil.valueWithDefault(padChar, ' ');
dir = miscUtil.valueWithDefault(dir, 'right');
stringColor = miscUtil.valueWithDefault(stringColor, '');
padColor = miscUtil.valueWithDefault(padColor, '');
var padlen = len - s.length;
switch(dir) {
case 'L' :
case 'left' :
s = new Array(padlen).join(padChar) + s;
s = padColor + new Array(padlen).join(padChar) + stringColor + s;
break;
case 'C' :
@ -130,16 +132,16 @@ function pad(s, len, padChar, dir) {
case 'both' :
var right = Math.ceil(padlen / 2);
var left = padlen - right;
s = new Array(left + 1).join(padChar) + s + new Array(right + 1).join(padChar);
s = padColor + new Array(left + 1).join(padChar) + stringColor + s + padColor + new Array(right + 1).join(padChar);
break;
case 'R' :
case 'right' :
s = s + new Array(padlen).join(padChar);
s = stringColor + s + padColor + new Array(padlen).join(padChar);
break;
default : break;
}
return s;
return stringColor + s;
}

View File

@ -20,7 +20,7 @@ function TextView(client, options) {
this.multiLine = this.options.multiLine || false;
this.fillChar = miscUtil.valueWithDefault(this.options.fillChar, ' ').substr(0, 1);
this.justify = this.options.justify || 'none';
this.justify = this.options.justify || 'right';
assert(!this.multiLine); // :TODO: not yet supported
@ -40,15 +40,25 @@ util.inherits(TextView, View);
TextView.prototype.redraw = function() {
TextView.super_.prototype.redraw.call(this);
var color = this.hasFocus ? this.getFocusColor() : this.getColor();
this.client.term.write(this.getANSIColor(color));
var ansiColor = this.getANSIColor(this.hasFocus ? this.getFocusColor() : this.getColor());
// :TODO: If using fillChar, don't just pad. switch to non-focus color & fill with |fillChar|
// :TODO: Apply justification
if(this.isPasswordTextStyle) {
this.client.term.write(strUtil.pad(new Array(this.text.length + 1).join(this.textMaskChar), this.dimens.width));
this.client.term.write(strUtil.pad(
new Array(this.text.length + 1).join(this.textMaskChar),
this.dimens.width,
this.fillChar,
this.justify,
ansiColor,
this.getANSIColor(this.getColor())));
} else {
this.client.term.write(strUtil.pad(this.text, this.dimens.width));
var text = strUtil.stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle);
this.client.term.write(strUtil.pad(
text,
this.dimens.width,
this.fillChar,
this.justify,
ansiColor,
this.getANSIColor(this.getColor())));
}
};
@ -57,6 +67,7 @@ TextView.prototype.setFocus = function(focused) {
this.redraw();
this.client.term.write(ansi.goto(this.position.x, this.position.y + this.text.length));
this.client.term.write(this.getANSIColor(this.getFocusColor()));
};
TextView.prototype.setText = function(text) {
@ -66,7 +77,7 @@ TextView.prototype.setText = function(text) {
this.text = this.text.substr(0, this.maxLength);
}
this.text = strUtil.stylizeString(this.text, this.textStyle);
this.text = strUtil.stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle);
if(!this.multiLine && !this.dimens.width) {
this.dimens.width = this.text.length;

View File

@ -14,6 +14,8 @@ function VerticalMenuView(client, options) {
var self = this;
this.itemSpacing = 3;
this.calculateDimens = function() {
if(!self.dimens || !self.dimens.width) {
var l = 0;
@ -26,15 +28,17 @@ function VerticalMenuView(client, options) {
self.dimens.width = l;
}
if(!self.dimens.height) {
//this.dimens.height = self.items.length
if(this.items.length > 0) {
this.dimens.height = (self.items.length * self.itemSpacing) - (self.itemSpacing - 1);
} else {
this.dimens.height = 0;
}
};
this.calculateDimens();
this.cacheXPositions = function() {
if(self.xPositionCacheExpired) {
this.cachePositions = function() {
if(self.positionCacheExpired) {
var count = this.items.length;
var x = self.position.x;
for(var i = 0; i < count; ++i) {
@ -44,12 +48,12 @@ function VerticalMenuView(client, options) {
self.items[i].xPosition = x;
}
self.xPositionCacheExpired = false;
self.positionCacheExpired = false;
}
};
this.drawItem = function(index) {
assert(!this.xPositionCacheExpired);
assert(!this.positionCacheExpired);
var item = self.items[index];
if(!item) {
@ -60,25 +64,11 @@ function VerticalMenuView(client, options) {
this.client.term.write(self.getANSIColor(
index === self.focusedItemIndex || item.selected ? self.getFocusColor() : self.getColor()));
var text = strUtil.stylizeString(item.text, item.hasFocus ? self.focusTextStyle : self.textStyle);
var text = strUtil.stylizeString(item.text, item.focused ? self.focusTextStyle : self.textStyle);
self.client.term.write(
strUtil.pad(text, this.dimens.width, this.fillChar, this.justify));
};
// :TODO: move to MenuView
this.moveSelection = function(fromIndex, toIndex) {
assert(!self.xPositionCacheExpired);
assert(fromIndex >= 0 && fromIndex <= self.items.length);
assert(toIndex >= 0 && toIndex <= self.items.length);
self.items[fromIndex].focused = false;
self.drawItem(fromIndex);
self.items[toIndex].focused = true;
self.focusedItemIndex = toIndex;
self.drawItem(toIndex);
};
}
util.inherits(VerticalMenuView, MenuView);
@ -86,19 +76,7 @@ util.inherits(VerticalMenuView, MenuView);
VerticalMenuView.prototype.setPosition = function(pos) {
VerticalMenuView.super_.prototype.setPosition.call(this, pos);
this.xPositionCacheExpired = true;
};
// :TODO: Could be moved to base with just this.cachePositions() ?
VerticalMenuView.prototype.redraw = function() {
VerticalMenuView.super_.prototype.redraw.call(this);
this.cacheXPositions();
var count = this.items.length;
for(var i = 0; i < count; ++i) {
this.drawItem(i);
}
this.positionCacheExpired = true;
};
VerticalMenuView.prototype.setFocus = function(focused) {
@ -139,7 +117,7 @@ VerticalMenuView.prototype.getViewData = function() {
VerticalMenuView.prototype.setItems = function(items) {
VerticalMenuView.super_.prototype.setItems.call(this, items);
this.xPositionCacheExpired = true;
this.cacheXPositions();
this.positionCacheExpired = true;
this.cachePositions();
this.calculateDimens();
};

View File

@ -32,7 +32,7 @@ function View(client, options) {
this.acceptsFocus = options.acceptsFocus || false;
this.acceptsInput = options.acceptsInput || false;
this.submit = this.acceptsInput ? options.acceptsInput || false : false;
//this.submit = this.acceptsInput ? options.acceptsInput || false : false;
this.position = { x : 0, y : 0 };
this.dimens = { height : 1, width : 0 };

View File

@ -3,7 +3,6 @@
//var art = require('../core/art.js');
var ansi = require('../core/ansi_term.js');
var lineEditor = require('../core/line_editor.js');
var art = require('../core/art.js');
var user = require('../core/user.js');
var theme = require('../core/theme.js');
@ -30,7 +29,7 @@ function entryPoint(client) {
//art.getArt('SO-CC1.ANS'/* 'MATRIX'*/, { types: ['.ans'], random: true}, function onArt(err, theArt) {
//client.user.properties.art_theme_id = '';
theme.getThemeArt('MCI_VM1.ANS', client.user.properties.art_theme_id, function onArt(err, theArt) {
theme.getThemeArt('MCI_FORM1.ANS', client.user.properties.art_theme_id, function onArt(err, theArt) {
//art.getArt('MATRIX_1.ANS', {}, function onArt(err, theArt) {
if(!err) {
@ -60,12 +59,18 @@ function entryPoint(client) {
});
var vc = new viewController.ViewController(client);
vc.on('submit', function onSubmit(formData) {
console.log(formData);
});
vc.loadFromMCIMap(mci);
//vc.getView(3).setText('New');
//vc.getView(4).setText('Login');
vc.setViewOrder();
vc.getView(1).submit = true;
vc.getView(1).setItems(['System Login', 'Apply', 'GTFO!']);
//vc.getView(1).submit = true;
//vc.getView(1).setItems(['System Login', 'Apply', 'GTFO!']);
vc.getView(2).submit = true;
vc.getView(3).setText('Apply');
vc.switchFocus(1);
});
}

BIN
test/ansi/MCI_FORM1.ANS Normal file

Binary file not shown.

Binary file not shown.