diff --git a/core/mask_edit_text_view.js b/core/mask_edit_text_view.js index 63aa2fef..999ad2fd 100644 --- a/core/mask_edit_text_view.js +++ b/core/mask_edit_text_view.js @@ -80,7 +80,7 @@ function MaskEditTextView(options) { } }; - this.getCursorEditYPosition = function() { + this.getEndOfTextYPosition = function() { return this.position.y + this.patternArrayPos; }; @@ -97,21 +97,6 @@ MaskEditTextView.maskPatternCharacterRegEx = { '&' : /[\w\d\s]/, // Any "printable" 32-126, 128-255 }; -MaskEditTextView.prototype.setFocus = function(focused) { - // :TODO: can't call super unless we want wasted redraw stuff. This seems sloppy & should probably be looked into - //MaskEditTextView.super_.prototype.setFocus.call(this, focused); - assert(this.acceptsFocus, 'View does not accept focus'); - - this.hasFocus = focused; - this.restoreCursor(); - - this.redraw(); - - // position & SGR for cursor - this.client.term.write(ansi.goto(this.position.x, this.getCursorEditYPosition())); - this.client.term.write(this.getFocusSGR()); -}; - MaskEditTextView.prototype.setMaskPattern = function(pattern) { this.dimens.width = pattern.length; @@ -143,7 +128,7 @@ MaskEditTextView.prototype.onKeyPress = function(key, isSpecial) { } this.redraw(); - this.client.term.write(ansi.goto(this.position.x, this.getCursorEditYPosition())); + this.client.term.write(ansi.goto(this.position.x, this.getEndOfTextYPosition())); } MaskEditTextView.super_.prototype.onKeyPress.call(this, key, isSpecial); @@ -163,7 +148,7 @@ MaskEditTextView.prototype.onSpecialKeyPress = function(keyName) { while(this.patternArrayPos > 0) { if(_.isRegExp(this.patternArray[this.patternArrayPos])) { this.text = this.text.substr(0, this.text.length - 1); - this.client.term.write(ansi.goto(this.position.x, this.getCursorEditYPosition() + 1)); + this.client.term.write(ansi.goto(this.position.x, this.getEndOfTextYPosition() + 1)); this.clientBackspace(); break; } diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index 13ce7802..03e50a34 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -1,21 +1,23 @@ /* jslint node: true */ 'use strict'; -var TextView = require('./text_view.js').TextView; -var EditTextView = require('./edit_text_view.js').EditTextView; -var ButtonView = require('./button_view.js').ButtonView; -var VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView; -var SpinnerMenuView = require('./spinner_menu_view.js').SpinnerMenuView; -var ToggleMenuView = require('./toggle_menu_view.js').ToggleMenuView; -var MaskEditTextView = require('./mask_edit_text_view.js').MaskEditTextView; -var Config = require('./config.js').config; -var ansi = require('./ansi_term.js'); +var TextView = require('./text_view.js').TextView; +var EditTextView = require('./edit_text_view.js').EditTextView; +var ButtonView = require('./button_view.js').ButtonView; +var VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView; +var SpinnerMenuView = require('./spinner_menu_view.js').SpinnerMenuView; +var ToggleMenuView = require('./toggle_menu_view.js').ToggleMenuView; +var MaskEditTextView = require('./mask_edit_text_view.js').MaskEditTextView; +var MultiLineEditTextView = require('./multi_line_edit_text_view.js').MultiLineEditTextView; -var packageJson = require('../package.json'); +var Config = require('./config.js').config; +var ansi = require('./ansi_term.js'); -var assert = require('assert'); -var os = require('os'); -var _ = require('lodash'); +var packageJson = require('../package.json'); + +var assert = require('assert'); +var os = require('os'); +var _ = require('lodash'); exports.MCIViewFactory = MCIViewFactory; @@ -125,6 +127,12 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { view = new MaskEditTextView(options); break; + // Multi Line Edit Text + case 'MT' : + // :TODO: apply params + view = new MultiLineEditTextView(options); + break; + // Pre-defined Label (Text View) case 'PL' : if(mci.args.length > 0) { diff --git a/core/multi_line_edit_text_view.js b/core/multi_line_edit_text_view.js new file mode 100644 index 00000000..3ea306d3 --- /dev/null +++ b/core/multi_line_edit_text_view.js @@ -0,0 +1,98 @@ +/* jslint node: true */ +'use strict'; + +var View = require('./view.js').View; +var miscUtil = require('./misc_util.js'); +var strUtil = require('./string_util.js'); +var ansi = require('./ansi_term.js'); + +var assert = require('assert'); +var _ = require('lodash'); + +exports.MultiLineEditTextView = MultiLineEditTextView; + +// +// Some resources & comparisons +// +// Enthral +// * https://github.com/M-griffin/Enthral/blob/master/src/msg_fse.cpp +// +// x84 +// * https://github.com/jquast/x84/blob/master/x84/bbs/editor.py +// +// + +function MultiLineEditTextView(options) { + View.call(this, options); + + var self = this; + + this.lines = []; // a given line is text...until EOL + this.topLineIndex = 0; + + this.drawViewableText = function() { + // + // v--- position.x/y + // +-----------------------------------+ <--- x + width + // | | + // | | + // | | + // +-----------------------------------+ + // ^--- position.y + height + // + // A given line in lines[] may need to take up 1:n physical lines + // due to wrapping / available space. + // + var x = self.position.x; + var bottom = x + self.dimens.height; + var lines; + var idx = self.topLineIndex; + + self.client.term.write(self.getSGR()); + + while(x < bottom) { + lines = self.getWordWrapLines(self.lines[idx]); + for(var y = 0; y < lines.length && x < bottom; ++y) { + self.client.term.write(ansi.goto(x, this.position.y)); + self.client.term.write(lines[y]); + ++x; + } + } + + }; + + this.getWordWrapLines = function(line) { + // + // Similar implementations: + // * http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/ + // * http://james.padolsey.com/snippets/wordwrap-for-javascript/ + // * http://phpjs.org/functions/wordwrap/ + // * https://github.com/jonschlinkert/word-wrap + // + /* + var re = new RegExp( + '(.{1,' + self.dimens.width + '}(\\s|$)|.{' + self.dimens.width + '}|.+$)', + 'g'); + */ + + var re = new RegExp('.{1,' + self.dimens.width + '}(\\s+|$)|\\S+?(\\s+|$)', 'g'); + + //return line.split(re); + return line.match(re) || []; + }; +} + +require('util').inherits(MultiLineEditTextView, View); + +MultiLineEditTextView.prototype.redraw = function() { + MultiLineEditTextView.super_.prototype.redraw.call(this); + + this.drawViewableText(); +} + +MultiLineEditTextView.prototype.setText = function(text) { + // :TODO: text.split(/\r\n|\n|\r/)) + this.lines = text.split(/\r?\n/); +} + + diff --git a/core/text_view.js b/core/text_view.js index 112860f9..34812b00 100644 --- a/core/text_view.js +++ b/core/text_view.js @@ -84,6 +84,11 @@ function TextView(options) { )); }; + this.getEndOfTextYPosition = function() { + var offset = Math.min(this.text.length, this.dimens.width); + return this.position.y + offset; + }; + this.setText(options.text || ''); } @@ -99,10 +104,8 @@ TextView.prototype.setFocus = function(focused) { TextView.super_.prototype.setFocus.call(this, focused); this.redraw(); - - // position & SGR for cursor - var offset = Math.min(this.text.length, this.dimens.width); - this.client.term.write(ansi.goto(this.position.x, this.position.y + offset)); + + this.client.term.write(ansi.goto(this.position.x, this.getEndOfTextYPosition())); this.client.term.write(this.getFocusSGR()); }; diff --git a/core/view.js b/core/view.js index 5b7c8656..b370b128 100644 --- a/core/view.js +++ b/core/view.js @@ -154,39 +154,6 @@ View.prototype.setWidth = function(width) { this.autoScale.width = false; }; -/* -View.prototype.setColor = function(color, bgColor, flags) { - if(_.isObject(color)) { - assert(_.has(color, 'fg')); - assert(_.has(color, 'bg')); - assert(_.has(color, 'flags')); - - this.color = color; - } else { - if(color) { - this.color.fg = color; - } - - if(bgColor) { - this.color.bg = bgColor; - } - - if(_.isNumber(flags)) { - this.color.flags = flags; - } - } - - // allow strings such as 'red', 'black', etc. to be passed - if(_.isString(this.color.fg)) { - this.color.fg = ansi.getFGColorValue(this.color.fg); - } - - if(_.isString(this.color.bg)) { - this.color.bg = ansi.getBGColorValue(this.color.bg); - } -}; -*/ - View.prototype.getSGR = function() { return this.ansiSGR; }; diff --git a/mods/menu.json b/mods/menu.json index 041d495d..3c2cebcb 100644 --- a/mods/menu.json +++ b/mods/menu.json @@ -211,6 +211,7 @@ "Single Line Text Editing Views", "Spinner & Toggle Views", "Mask Edit Views", + "Multi Line Text Editor", "Vertical Menu Views", "Horizontal Menu Views", "Art Display", @@ -238,7 +239,11 @@ "action" : "@menu:demoMaskEditView" }, { - "value" : { "1" : 5 }, + "value" : { "1" : 3 }, + "action" : "@menu:demoMultiLineEditTextView" + }, + { + "value" : { "1" : 6 }, "action" : "@menu:demoArtDisplay" } ] @@ -338,6 +343,31 @@ "maskPattern" : "##/##/##", "styleSGR1" : "|00|30|01", "styleSGR2" : "|00|45|01" + }, + "BT5" : { + "text" : "< Back", + "submit" : [ "esc" ] + } + } + } + } + } + }, + "demoMultiLineEditTextView" : { + "art" : "demo_multi_line_edit_text_view1.ans", + "options" : { "cls" : true }, + "form" : { + "0" : { + "BT5MT1" : { + "mci" : { + "MT1" : { + "width" : 50, + "height" : 5, + "text" : "Now all the time I was watching this, I was beginning to get very aware of like not feeling all that well, but I tried to forget this, concentrating on the next film, which jumped right away on a young devotchka, who was being given the old in-out, in-out, first by one malchick, then another, then another. This seemed real, very real, though if you thought about it properly you couldn't imagine lewdies actually agreeing to having all this done to them in a film, and if these films were made by the good, or the State, you couldn't imagine them being allowed to take these films, without like interfering with what was going on.\nWhen it came to the sixth or seventh malchick, leering and smecking and then going into it, I began to feel really sick. But I could not shut my glazzies and even if I tried to move my glazballs about I still not get out of the line of fire of this picture." + }, + "BT5" : { + "text" : "< Back", + "submit" : [ "esc" ] } } }