* Fixed double extra view redraws

* Redraw views when all of them are ready @ MCI init
* Moved a lot of properties of views to menu.json/prompts
* Allow scrolling editor for EditTextView
* New @config:path.to.property for menu.json view properties/etc.
This commit is contained in:
Bryan Ashby 2015-04-26 20:46:16 -06:00
parent 5288643189
commit 7a643150e7
10 changed files with 233 additions and 71 deletions

View File

@ -8,6 +8,7 @@ var assert = require('assert');
exports.parseAsset = parseAsset; exports.parseAsset = parseAsset;
exports.getArtAsset = getArtAsset; exports.getArtAsset = getArtAsset;
exports.resolveConfigAsset = resolveConfigAsset;
var ALL_ASSETS = [ var ALL_ASSETS = [
'art', 'art',
@ -15,6 +16,7 @@ var ALL_ASSETS = [
'method', 'method',
'systemMethod', 'systemMethod',
'prompt', 'prompt',
'config',
]; ];
var ASSET_RE = new RegExp('\\@(' + ALL_ASSETS.join('|') + ')\\:([\\w\\d\\.]*)(?:\\/([\\w\\d\\_]+))*'); var ASSET_RE = new RegExp('\\@(' + ALL_ASSETS.join('|') + ')\\:([\\w\\d\\.]*)(?:\\/([\\w\\d\\_]+))*');
@ -36,7 +38,7 @@ function parseAsset(s) {
} }
} }
function getArtAsset(art, cb) { function getArtAsset(art) {
if(!_.isString(art)) { if(!_.isString(art)) {
return null; return null;
} }
@ -53,3 +55,22 @@ function getArtAsset(art, cb) {
}; };
} }
} }
function resolveConfigAsset(from) {
var asset = parseAsset(from);
if(asset) {
assert('config' === asset.type);
var path = asset.asset.split('.');
var conf = Config;
for(var i = 0; i < path.length; ++i) {
if(_.isUndefined(conf[path[i]])) {
return from;
}
conf = conf[path[i]];
}
return conf;
} else {
return from;
}
}

View File

@ -83,7 +83,7 @@ function getDefaultConfig() {
usernameMax : 22, usernameMax : 22,
usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+]+$', usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+]+$',
passwordMin : 6, passwordMin : 6,
passwordMax : 256, passwordMax : 128,
requireActivation : true, // require SysOp activation? requireActivation : true, // require SysOp activation?
invalidUsernames : [], invalidUsernames : [],
}, },

View File

@ -14,9 +14,11 @@ function EditTextView(options) {
options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true);
options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true);
options.resizable = false; options.resizable = false;
TextView.call(this, options); TextView.call(this, options);
this.cursorPos = { x : 0 };
this.clientBackspace = function() { this.clientBackspace = function() {
this.client.term.write( this.client.term.write(
'\b' + this.getANSIColor(this.getColor()) + this.fillChar + '\b' + this.getANSIColor(this.getFocusColor())); '\b' + this.getANSIColor(this.getColor()) + this.fillChar + '\b' + this.getANSIColor(this.getFocusColor()));
@ -33,15 +35,22 @@ EditTextView.prototype.onKeyPress = function(key, isSpecial) {
assert(1 === key.length); assert(1 === key.length);
// :TODO: how to handle justify left/center? // :TODO: how to handle justify left/center?
if(!_.isNumber(this.maxLength) || this.text.length < this.maxLength) { if(this.text.length < this.maxLength) {
key = strUtil.stylizeString(key, this.textStyle); key = strUtil.stylizeString(key, this.textStyle);
this.text += key; this.text += key;
if(this.textMaskChar) { if(this.text.length > this.dimens.width) {
this.client.term.write(this.textMaskChar); // no shortcuts - redraw the view
this.redraw();
} else { } else {
this.client.term.write(key); this.cursorPos.x += 1;
if(this.textMaskChar) {
this.client.term.write(this.textMaskChar);
} else {
this.client.term.write(key);
}
} }
} }
@ -54,9 +63,18 @@ EditTextView.prototype.onSpecialKeyPress = function(keyName) {
if(this.isSpecialKeyMapped('backspace', keyName)) { if(this.isSpecialKeyMapped('backspace', keyName)) {
if(this.text.length > 0) { if(this.text.length > 0) {
this.text = this.text.substr(0, this.text.length - 1); this.text = this.text.substr(0, this.text.length - 1);
this.clientBackspace();
if(this.text.length >= this.dimens.width) {
this.redraw();
} else {
this.cursorPos.x -= 1;
if(this.cursorPos.x >= 0) {
this.clientBackspace();
}
}
} }
} }
EditTextView.super_.prototype.onSpecialKeyPress.call(this, keyName); EditTextView.super_.prototype.onSpecialKeyPress.call(this, keyName);
}; };

View File

@ -70,6 +70,17 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
return false; return false;
} }
function setWidth(pos) {
if(mci.args.length > pos && mci.args[pos].length > 0) {
if(!_.isObject(options.dimens)) {
options.dimens = {};
}
options.dimens.width = parseInt(mci.args[pos], 10);
return true;
}
return false;
}
function setFocusOption(pos, name) { function setFocusOption(pos, name) {
if(mci.focusArgs && mci.focusArgs.length > pos && mci.focusArgs[pos].length > 0) { if(mci.focusArgs && mci.focusArgs.length > pos && mci.focusArgs[pos].length > 0) {
options[name] = mci.focusArgs[pos]; options[name] = mci.focusArgs[pos];
@ -82,30 +93,31 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
case 'TL' : case 'TL' :
setOption(0, 'textStyle'); setOption(0, 'textStyle');
setOption(1, 'justify'); setOption(1, 'justify');
setWidth(2);
/*
if(setOption(2, 'maxLength')) { if(setOption(2, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10); options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength }; options.dimens = { width : options.maxLength };
} }
*/
view = new TextView(options); view = new TextView(options);
break; break;
// Edit Text // Edit Text
case 'ET' : case 'ET' :
setWidth(0);
/*
if(setOption(0, 'maxLength')) { if(setOption(0, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10); // ensure number options.maxLength = parseInt(options.maxLength, 10); // ensure number
options.dimens = { width : options.maxLength }; options.dimens = { width : options.maxLength };
} }
*/
setOption(1, 'textStyle'); setOption(1, 'textStyle');
setFocusOption(0, 'focusTextStyle');
if(options.textStyle === 'P') {
// Assign the proper password character / text mask
assert(_.isObject(this.client.currentThemeInfo));
options.textMaskChar = this.client.currentThemeInfo.getPasswordChar();
}
setFocusOption(0, 'focusTextStyle');
view = new EditTextView(options); view = new EditTextView(options);
break; break;
@ -117,11 +129,14 @@ MCIViewFactory.prototype.createFromMCI = function(mci) {
if(options.text) { if(options.text) {
setOption(1, 'textStyle'); setOption(1, 'textStyle');
setOption(2, 'justify'); setOption(2, 'justify');
setWidth(3);
/*
if(setOption(3, 'maxLength')) { if(setOption(3, 'maxLength')) {
options.maxLength = parseInt(options.maxLength, 10); options.maxLength = parseInt(options.maxLength, 10);
options.dimens = { width : options.maxLength }; options.dimens = { width : options.maxLength };
} }
*/
view = new TextView(options); view = new TextView(options);
} }

View File

@ -148,6 +148,7 @@ function getFormConfigByIDAndMap(menuConfig, formId, mciMap, cb) {
var formForId = menuConfig.form[formId]; var formForId = menuConfig.form[formId];
var mciReqKey = _.sortBy(Object.keys(mciMap), String).join(''); var mciReqKey = _.sortBy(Object.keys(mciMap), String).join('');
Log.trace( { mciKey : mciReqKey }, 'Looking for MCI configuration key');
if(_.isObject(formForId[mciReqKey])) { if(_.isObject(formForId[mciReqKey])) {
cb(null, formForId[mciReqKey]); cb(null, formForId[mciReqKey]);
return; return;

View File

@ -5,8 +5,10 @@ var View = require('./view.js').View;
var miscUtil = require('./misc_util.js'); var miscUtil = require('./misc_util.js');
var strUtil = require('./string_util.js'); var strUtil = require('./string_util.js');
var ansi = require('./ansi_term.js'); var ansi = require('./ansi_term.js');
var util = require('util'); var util = require('util');
var assert = require('assert'); var assert = require('assert');
var _ = require('lodash');
exports.TextView = TextView; exports.TextView = TextView;
@ -17,27 +19,52 @@ function TextView(options) {
if(options.maxLength) { if(options.maxLength) {
this.maxLength = options.maxLength; this.maxLength = options.maxLength;
} else {
this.maxLength = this.client.term.termWidth - this.position.x;
} }
this.multiLine = options.multiLine || false; this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1);
this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1); this.justify = options.justify || 'right';
this.justify = options.justify || 'right'; this.resizable = miscUtil.valueWithDefault(options.resizable, true);
this.resizable = miscUtil.valueWithDefault(options.resizable, true); this.horizScroll = miscUtil.valueWithDefault(options.horizScroll, true);
//this.inputType = options.inputType || 'normal';
this.isPasswordTextStyle = 'P' === this.textStyle || 'password' === this.textStyle; if(_.isString(options.textMaskChar) && 1 === options.textMaskChar.length) {
this.textMaskChar = options.textMaskChar;
assert(!this.multiLine); // :TODO: not yet supported
if(!this.multiLine) {
this.dimens.height = 1;
} }
this.dimens.height = 1;
this.drawText = function(s) { this.drawText = function(s) {
var ansiColor = this.getANSIColor(this.hasFocus ? this.getFocusColor() : this.getColor());
if(this.isPasswordTextStyle) { //
// ABCDEFGHIJK
// |ABCDEFG| ^_ length
// ^-- dimens.width
//
var textToDraw = _.isString(this.textMaskChar) ?
new Array(s.length + 1).join(this.textMaskChar) :
strUtil.stylizeString(s, this.hasFocus ? this.focusTextStyle : this.textStyle);
if(textToDraw.length > this.dimens.width) {
// XXXXXXXXXXXXXXXXX
// this is the text but too long
// text but too long
if(this.horizScroll) {
textToDraw = textToDraw.substr(textToDraw.length - this.dimens.width, textToDraw.length);//0, this.dimens.width);//textToDraw.length - (this.dimens.width + 1));
}
}
var textAnsiColor = this.getANSIColor(this.hasFocus ? this.getFocusColor() : this.getColor());
var fillAnsiColor = this.getANSIColor(this.getColor());
//console.log(textToDraw)
///console.log(this.dimens.width + 1)
this.client.term.write(strUtil.pad(textToDraw, this.dimens.width + 1, this.fillChar, this.justify, textAnsiColor, fillAnsiColor));
/*
if(_.isString(this.textMaskChar)) {
this.client.term.write(strUtil.pad( this.client.term.write(strUtil.pad(
new Array(s.length + 1).join(this.textMaskChar), new Array(s.length + 1).join(this.textMaskChar),
this.dimens.width + 1, this.dimens.width + 1,
@ -55,13 +82,10 @@ function TextView(options) {
ansiColor, ansiColor,
this.getANSIColor(this.getColor()))); this.getANSIColor(this.getColor())));
} }
*/
}; };
this.setText(options.text || ''); this.setText(options.text || '');
if(this.isPasswordTextStyle) {
this.textMaskChar = miscUtil.valueWithDefault(options.textMaskChar, '*').substr(0, 1);
}
} }
util.inherits(TextView, View); util.inherits(TextView, View);
@ -99,14 +123,8 @@ TextView.prototype.setText = function(text) {
this.text = strUtil.stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle); this.text = strUtil.stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle);
/*if(!this.multiLine && !this.dimens.width) { if(this.resizable) {
this.dimens.width = this.text.length; this.dimens.width = this.text.length + widthDelta;
}*/
if(!this.multiLine) {
if(this.resizable) {
this.dimens.width = this.text.length + widthDelta;
}
} }
this.redraw(); this.redraw();

View File

@ -49,9 +49,6 @@ function View(options) {
this.setPosition(options.position); this.setPosition(options.position);
} }
// this.isPasswordTextStyle = 'P' === this.textStyle || 'password' === this.textStyle;
// :TODO: Don't allow width/height > client.term // :TODO: Don't allow width/height > client.term
if(options.dimens && options.dimens.height) { if(options.dimens && options.dimens.height) {
this.dimens.height = options.dimens.height; this.dimens.height = options.dimens.height;

View File

@ -120,6 +120,7 @@ function ViewController(options) {
}; };
this.getLogFriendlyFormData = function(formData) { this.getLogFriendlyFormData = function(formData) {
// :TODO: these fields should be part of menu.json sensitiveMembers[]
var safeFormData = _.cloneDeep(formData); var safeFormData = _.cloneDeep(formData);
if(safeFormData.value.password) { if(safeFormData.value.password) {
safeFormData.value.password = '*****'; safeFormData.value.password = '*****';
@ -149,8 +150,6 @@ function ViewController(options) {
view.on('action', self.viewActionListener); view.on('action', self.viewActionListener);
self.addView(view); self.addView(view);
view.redraw(); // :TODO: fix double-redraw if this is the item we set focus to!
} }
nextItem(null); nextItem(null);
@ -162,21 +161,54 @@ function ViewController(options) {
}; };
this.setViewPropertiesFromMCIConf = function(view, conf) { this.setViewPropertiesFromMCIConf = function(view, conf) {
if(_.isBoolean(conf.submit)) {
view.submit = conf.submit; function getViewProp(propName) {
} else if(_.isArray(conf.submit) && conf.submit.length > 0) { if(conf[propName]) {
view.submit = true; return asset.resolveConfigAsset(conf[propName]);
}
}
function setSimpleViewProp(propName) {
var propValue = getViewProp(propName);
if(propValue) {
view[propName] = propValue;
}
}
var value;
value = getViewProp('items');
if(value) {
view.setItems(value);
}
value = getViewProp('text');
if(value) {
view.setText(value);
}
setSimpleViewProp('textStyle');
setSimpleViewProp('focusTextStyle');
setSimpleViewProp('fillChar');
setSimpleViewProp('maxLength');
value = getViewProp('textMaskChar');
if(_.isString(value)) {
view.textMaskChar = value.substr(0, 1);
} else if(value && true === value) {
//
// Option that should normally be used in order to
// get the password character from Config/theme
//
view.textMaskChar = self.client.currentThemeInfo.getPasswordChar();
}
value = getViewProp('submit');
if(_.isBoolean(value)) {
view.submit = value;
} else { } else {
view.submit = false; view.submit = _.isArray(value) && value.length > 0;
}
//view.submit = conf.submit || false;
if(_.isArray(conf.items)) {
view.setItems(conf.items);
}
if(_.isString(conf.text)) {
view.setText(conf.text);
} }
if(_.isString(conf.argName)) { if(_.isString(conf.argName)) {
@ -196,6 +228,13 @@ function ViewController(options) {
assert(!isNaN(viewId)); assert(!isNaN(viewId));
var view = self.getView(viewId); var view = self.getView(viewId);
if(!view) {
Log.warn( { viewId : viewId }, 'Cannot find view');
nextItem(null);
return;
}
var mciConf = config.mci[mci]; var mciConf = config.mci[mci];
self.setViewPropertiesFromMCIConf(view, mciConf); self.setViewPropertiesFromMCIConf(view, mciConf);
@ -328,6 +367,7 @@ ViewController.prototype.setViewOrder = function(order) {
} }
}; };
/*
ViewController.prototype.loadFromMCIMap = function(mciMap) { ViewController.prototype.loadFromMCIMap = function(mciMap) {
var factory = new MCIViewFactory(this.client); var factory = new MCIViewFactory(this.client);
var self = this; var self = this;
@ -343,6 +383,7 @@ ViewController.prototype.loadFromMCIMap = function(mciMap) {
} }
}); });
}; };
*/
ViewController.prototype.loadFromPromptConfig = function(options, cb) { ViewController.prototype.loadFromPromptConfig = function(options, cb) {
assert(_.isObject(options)); assert(_.isObject(options));
@ -375,6 +416,15 @@ ViewController.prototype.loadFromPromptConfig = function(options, cb) {
callback(null); callback(null);
}, },
function drawAllViews(callback) {
for(var id in self.views) {
if(initialFocusId === id) {
continue; // will draw @ focus
}
self.views[id].redraw();
}
callback(null);
},
function setInitialViewFocus(callback) { function setInitialViewFocus(callback) {
if(initialFocusId) { if(initialFocusId) {
self.switchFocus(initialFocusId); self.switchFocus(initialFocusId);
@ -501,6 +551,15 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) {
callback(null); callback(null);
}, },
function drawAllViews(callback) {
for(var id in self.views) {
if(initialFocusId === id) {
continue; // will draw @ focus
}
self.views[id].redraw();
}
callback(null);
},
function setInitialViewFocus(callback) { function setInitialViewFocus(callback) {
if(initialFocusId) { if(initialFocusId) {
self.switchFocus(initialFocusId); self.switchFocus(initialFocusId);

View File

@ -99,20 +99,41 @@
"form" : { "form" : {
"0" : { "0" : {
"BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : { "BT12BT13ET1ET10ET2ET3ET4ET5ET6ET7ET8ET9TL11" : {
//
// "beforeViewsDraw" : "@method:location.js/myBeforeViewsDraw" -> myBeforeViewsDraw(views)
"mci" : { "mci" : {
"ET1" : { "ET1" : {
"focus" : true, "focus" : true,
"argName" : "username" "argName" : "username"
}, },
"ET2" : { "argName" : "realName" }, "ET2" : {
"argName" : "realName",
"maxLength" : "@config:users.usernameMax"
},
"ET3" : { "argName" : "age" }, "ET3" : { "argName" : "age" },
"ET4" : { "argName" : "sex" }, "ET4" : { "argName" : "sex" },
"ET5" : { "argName" : "location" }, "ET5" : { "argName" : "location" },
"ET6" : { "argName" : "affils" }, "ET6" : {
"ET7" : { "argName" : "email" }, "argName" : "affils",
"ET8" : { "argName" : "web" }, "maxLength" : 30
"ET9" : { "argName" : "password" }, },
"ET10" : { "argName" : "passwordConfirm" }, "ET7" : {
"argName" : "email",
"maxLength" : 255
},
"ET8" : {
"argName" : "web",
"maxLength" : 255
},
"ET9" : {
"argName" : "password",
"password" : true
},
"ET10" : {
"argName" : "passwordConfirm",
"password" : true,
"maxLength" : "@config:users.passwordMax"
},
"BT12" : { "BT12" : {
"submit" : true, "submit" : true,
"text" : "Apply" "text" : "Apply"
@ -162,7 +183,8 @@
"VM1" : { "VM1" : {
"mci" : { "mci" : {
"VM1" : { "VM1" : {
"items" : [ "Edit Text View", "Other" ] "items" : [ "Edit Text View", "Other" ],
"focusTextStyle" : "U"
} }
}, },
"submit" : { "submit" : {
@ -182,7 +204,17 @@
"options" : { "cls" : true }, "options" : { "cls" : true },
"form" : { "form" : {
"0" : { "0" : {
"CB4ET1ET2ET3ET4ET5" : { "CB4ET1ET2ET3ET5" : {
"mci" : {
"ET1" : { "maxLength" : 1 },
"ET2" : { "maxLength" : 1 },
"ET3" : { "maxLength" : 1 },
"ET5" : {
"password" : true,
"submit" : [ "esc" ],
"fillChar" : "#"
}
}
} }
} }
} }

View File

@ -7,7 +7,8 @@
}, },
"ET2" : { "ET2" : {
"submit" : true, "submit" : true,
"argName" : "password" "argName" : "password",
"password" : true
} }
} }
} }