* Rewrite of Views functional. WIP more to come

This commit is contained in:
NuSkooler 2014-10-22 23:41:00 -06:00
parent 51d15659d5
commit 381ded17a6
6 changed files with 328 additions and 12 deletions

66
core/edit_text_view.js Normal file
View File

@ -0,0 +1,66 @@
/* jslint node: true */
'use strict';
var TextView = require('./text_view.js').TextView;
var miscUtil = require('./misc_util.js');
var strUtil = require('./string_util.js');
var util = require('util');
var assert = require('assert');
exports.EditTextView = EditTextView;
function EditTextView(client, options) {
options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true);
options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true);
TextView.call(this, client, options);
this.clientBackspace = function() {
this.client.term.write('\b \b');
};
}
util.inherits(EditTextView, TextView);
EditTextView.prototype.onKeyPress = function(key, isSpecial) {
assert(this.hasFocus);
assert(this.acceptsInput);
if(isSpecial) {
return;
}
assert(1 === key.length);
if(this.text.length < this.options.maxLength) {
key = strUtil.stylizeString(key, this.textStyle);
this.text += key;
if(this.textMaskChar) {
this.client.term.write(this.textMaskChar);
} else {
this.client.term.write(key);
}
}
};
EditTextView.prototype.onSpecialKeyPress = function(keyName) {
assert(this.hasFocus);
assert(this.acceptsInput);
assert(this.specialKeyMap);
if(this.isSpecialKeyMapped('backspace', keyName)) {
if(this.text.length > 0) {
this.text = this.text.substr(0, this.text.length - 1);
this.clientBackspace();
}
} else if(this.isSpecialKeyMapped('enter', keyName)) {
if(this.multiLine) {
} else {
this.emit('action', 'accepted');
}
} else if(this.isSpecialKeyMapped('next', keyName)) {
this.emit('action', 'next');
}
};

44
core/mci_view_factory.js Normal file
View File

@ -0,0 +1,44 @@
/* jslint node: true */
'use strict';
var TextView = require('./text_view.js').TextView;
var EditTextView = require('./edit_text_view.js').EditTextView;
var assert = require('assert');
exports.MCIViewFactory = MCIViewFactory;
function MCIViewFactory(client) {
this.client = client;
}
MCIViewFactory.prototype.createFromMCI = function(mci) {
assert(mci.code);
assert(mci.id > 0);
assert(mci.position);
var view;
var options = {
id : mci.id,
color : mci.color,
focusColor : mci.focusColor,
position : { x : mci.position[0], y : mci.position[1] },
};
switch(mci.code) {
case 'EV' :
if(mci.args.length > 0) {
options.maxLength = mci.args[0];
options.dimens = { width : options.maxLength };
}
if(mci.args.length > 1) {
options.textStyle = mci.args[1];
}
view = new EditTextView(this.client, options);
break;
}
return view;
};

View File

@ -33,7 +33,7 @@ function TextView(client, options) {
this.isPasswordTextStyle = 'P' === self.textStyle || 'password' === self.textStyle; this.isPasswordTextStyle = 'P' === self.textStyle || 'password' === self.textStyle;
if(this.isPasswordTextStyle) { if(this.isPasswordTextStyle) {
this.passwordMaskChar = miscUtil.valueWithDefault(this.options.passwordMaskChar, '*').substr(0, 1); this.textMaskChar = miscUtil.valueWithDefault(this.options.textMaskChar, '*').substr(0, 1);
} }
} }
@ -47,12 +47,20 @@ TextView.prototype.redraw = function() {
this.client.term.write(ansi.sgr(color.flags, color.fg, color.bg)); this.client.term.write(ansi.sgr(color.flags, color.fg, color.bg));
if(this.isPasswordTextStyle) { if(this.isPasswordTextStyle) {
this.client.term.write(strUtil.pad(new Array(this.text.length).join(this.passwordMaskChar), this.dimens.width)); this.client.term.write(strUtil.pad(new Array(this.text.length).join(this.textMaskChar), this.dimens.width));
} else { } else {
this.client.term.write(strUtil.pad(this.text, this.dimens.width)); this.client.term.write(strUtil.pad(this.text, this.dimens.width));
} }
}; };
TextView.prototype.setFocus = function(focused) {
TextView.super_.prototype.setFocus.call(this, focused);
this.client.term.write(ansi.goto(this.position.x, this.position.y));
this.redraw();
this.client.term.write(ansi.goto(this.position.x, this.position.y + this.text.length));
};
TextView.prototype.setText = function(text) { TextView.prototype.setText = function(text) {
this.text = text; this.text = text;
@ -62,7 +70,7 @@ TextView.prototype.setText = function(text) {
this.text = strUtil.stylizeString(this.text, this.textStyle); this.text = strUtil.stylizeString(this.text, this.textStyle);
if(!this.multiLine) { if(!this.multiLine && !this.dimens.width) {
this.dimens.width = this.text.length; this.dimens.width = this.text.length;
} }
}; };

View File

@ -26,16 +26,21 @@ function View(client, options) {
this.client = client; this.client = client;
this.options = options || {}; this.options = options || {};
this.acceptsFocus = false; this.acceptsFocus = options.acceptsFocus || false;
this.acceptsInput = false; this.acceptsInput = options.acceptsInput || false;
this.position = { x : 0, y : 0 }; this.position = { x : 0, y : 0 };
this.dimens = { height : 1, width : 0 }; this.dimens = { height : 1, width : 0 };
if(this.options.id) {
this.setId(this.options.id);
}
if(this.options.position) { if(this.options.position) {
this.setPosition(this.options.position); this.setPosition(this.options.position);
} }
// :TODO: Don't allow width/height > client.term
if(this.options.dimens && this.options.dimens.height) { if(this.options.dimens && this.options.dimens.height) {
this.dimens.height = this.options.dimens.height; this.dimens.height = this.options.dimens.height;
} }
@ -50,10 +55,17 @@ function View(client, options) {
if(this.acceptsInput) { if(this.acceptsInput) {
this.specialKeyMap = this.options.specialKeyMap || VIEW_SPECIAL_KEY_MAP_DEFAULT; this.specialKeyMap = this.options.specialKeyMap || VIEW_SPECIAL_KEY_MAP_DEFAULT;
} }
this.isSpecialKeyMapped = function(keySet, keyName) {
return this.specialKeyMap[keySet].indexOf(keyName) > -1;
};
} }
util.inherits(View, events.EventEmitter); util.inherits(View, events.EventEmitter);
View.prototype.setId = function(id) {
this.id = id;
};
View.prototype.setPosition = function(pos) { View.prototype.setPosition = function(pos) {
// //
@ -66,14 +78,13 @@ View.prototype.setPosition = function(pos) {
this.position.x = pos.x; this.position.x = pos.x;
this.position.y = pos.y; this.position.y = pos.y;
} else if(2 === arguments.length) { } else if(2 === arguments.length) {
var x = parseInt(arguments[0], 10); this.position.x = parseInt(arguments[0], 10);
var y = parseInt(arguments[1], 10); this.position.y = parseInt(arguments[1], 10);
if(!isNaN(x) && !isNaN(y)) {
this.position.x = x;
this.position.y = y;
}
} }
assert(!(isNaN(this.position.x)));
assert(!(isNaN(this.position.y)));
assert(this.position.x > 0 && this.position.x < this.client.term.termHeight); assert(this.position.x > 0 && this.position.x < this.client.term.termHeight);
assert(this.position.y > 0 && this.position.y < this.client.term.termWidth); assert(this.position.y > 0 && this.position.y < this.client.term.termWidth);
}; };

151
core/view_controller.js Normal file
View File

@ -0,0 +1,151 @@
/* jslint node: true */
'use strict';
var events = require('events');
var util = require('util');
var assert = require('assert');
var MCIViewFactory = require('./mci_view_factory.js').MCIViewFactory;
exports.ViewController = ViewController;
function ViewController(client) {
events.EventEmitter.call(this);
var self = this;
this.client = client;
this.views = {}; // map of ID -> view
this.onClientKeyPress = function(key, isSpecial) {
if(isSpecial) {
return;
}
if(self.focusedView && self.focusedView.acceptsInput) {
key = 'string' === typeof key ? key : key.toString();
self.focusedView.onKeyPress(key, isSpecial);
}
};
this.onClientSpecialKeyPress = function(keyName) {
if(self.focusedView && self.focusedView.acceptsInput) {
self.focusedView.onSpecialKeyPress(keyName);
}
};
this.onViewAction = function(action) {
switch(action) {
case 'next' :
self.emit('action', { view : this, action : action });
self.nextFocus();
break;
case 'accepted' :
// :TODO: check if id is submit, etc.
self.nextFocus();
break;
}
};
this.attachClientEvents();
}
util.inherits(ViewController, events.EventEmitter);
ViewController.prototype.attachClientEvents = function() {
if(this.attached) {
return;
}
this.client.on('key press', this.onClientKeyPress);
this.client.on('special key', this.onClientSpecialKeyPress);
this.attached = true;
};
ViewController.prototype.detachClientEvents = function() {
if(!this.attached) {
return;
}
this.client.removeListener('key press', this.onClientKeyPress);
this.client.removeListener('special key', this.onClientSpecialKeyPress);
this.attached = false;
};
ViewController.prototype.viewExists = function(id) {
return id in this.views;
};
ViewController.prototype.addView = function(view) {
assert(!this.viewExists(view.id), 'View with ID ' + view.id + ' already exists');
this.views[view.id] = view;
};
ViewController.prototype.getView = function(id) {
return this.views[id];
};
ViewController.prototype.switchFocus = function(id) {
if(this.focusedView && this.focusedView.acceptsFocus) {
this.focusedView.setFocus(false);
}
var view = this.getView(id);
if(view && view.acceptsFocus) {
this.focusedView = view;
this.focusedView.setFocus(true);
}
// :TODO: Probably log here
};
ViewController.prototype.nextFocus = function() {
if(!this.focusedView) {
this.switchFocus(this.views[this.firstId].id);
} else {
var nextId = this.views[this.focusedView.id].nextId;
this.switchFocus(nextId);
}
};
ViewController.prototype.setViewOrder = function(order) {
var viewIdOrder = order || [];
if(0 === viewIdOrder.length) {
for(var id in this.views) {
viewIdOrder.push(id);
}
viewIdOrder.sort();
}
var view;
var count = viewIdOrder.length - 1;
for(var i = 0; i < count; ++i) {
this.views[viewIdOrder[i]].nextId = viewIdOrder[i + 1];
}
this.firstId = viewIdOrder[0];
var lastId = viewIdOrder[viewIdOrder.length - 1];
this.views[lastId].nextId = this.firstId;
};
ViewController.prototype.loadFromMCIMap = function(mciMap) {
var factory = new MCIViewFactory(this.client);
var self = this;
Object.keys(mciMap).forEach(function onMciEntry(name) {
var mci = mciMap[name];
var view = factory.createFromMCI(mci);
if(view) {
view.on('action', self.onViewAction);
self.addView(view);
}
});
};

View File

@ -9,6 +9,8 @@ var user = require('../core/user.js');
//var view = require('../core/view.js'); //var view = require('../core/view.js');
var textView = require('../core/text_view.js'); var textView = require('../core/text_view.js');
var editTextView = require('../core/edit_text_view.js');
var viewController = require('../core/view_controller.js');
exports.moduleInfo = { exports.moduleInfo = {
name : 'Matrix', name : 'Matrix',
@ -34,15 +36,49 @@ function entryPoint(client) {
return; return;
} }
/*
var tv = new textView.TextView(client, { var tv = new textView.TextView(client, {
position : [5, 5], position : [5, 5],
text : 'Hello, World!', text : 'Hello, World!',
textStyle : 'password', textStyle : 'password',
maxLength : 10 maxLength : 10,
id : 1,
}); });
tv.redraw(); tv.redraw();
var etv = new editTextView.EditTextView(client, {
position : [10, 10],
textStyle : 'upper',
maxLength : 20,
dimens : { width : 30 },
text : 'default',
color : { flags : 0, fg : 31, bg : 40 },
focusColor : { flags : 1, fg : 37, bg : 44 },
id : 2,
});
etv.redraw();*/
var vc = new viewController.ViewController(client);
vc.loadFromMCIMap(mci);
vc.setViewOrder();
vc.switchFocus(1);
//vc.addView(etv);
//vc.switchFocus(2);
/*
client.on('key press', function onKp(key, isSpecial) {
key = 'string' === typeof key ? key : key.toString();
etv.onKeyPress(key, isSpecial);
});
client.on('special key', function onSK(keyName) {
etv.onSpecialKeyPress(keyName);
});
*/
/* /*
var vc = new view.ViewsController(client); var vc = new view.ViewsController(client);
vc.loadFromMCIMap(mci); vc.loadFromMCIMap(mci);