From a6d00b05a76f7718e8c4574bdc5dab6861f53c62 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sat, 19 Sep 2015 22:55:09 -0600 Subject: [PATCH] * Artwork for NU-MAYA help, updated quote builder, etc. * Fix some typos * Fix message getQuoteLines() * Quote builder fully functional * MLTEV emits more information in position events * Action keys can how handle plain characters that don't have full key object, e.g. "?" * Hot keys for a lot of stuff * WIP work on focus issue in VC. --- core/fse.js | 15 +++++- core/menu_util.js | 2 + core/message.js | 24 ++++++--- core/multi_line_edit_text_view.js | 37 ++++++------- core/view_controller.js | 64 +++++++++++++---------- mods/menu.hjson | 84 +++++++++++++++++++++--------- mods/msg_area_post_fse.js | 5 ++ mods/msg_area_view_fse.js | 6 ++- mods/themes/NU-MAYA/MSGEHLP.ANS | Bin 0 -> 3485 bytes mods/themes/NU-MAYA/MSGQUOT.ANS | Bin 310 -> 516 bytes mods/themes/NU-MAYA/MSGVHLP.ANS | Bin 0 -> 3570 bytes 11 files changed, 157 insertions(+), 80 deletions(-) create mode 100644 mods/themes/NU-MAYA/MSGEHLP.ANS create mode 100644 mods/themes/NU-MAYA/MSGVHLP.ANS diff --git a/core/fse.js b/core/fse.js index 50755aae..a2b20f07 100644 --- a/core/fse.js +++ b/core/fse.js @@ -446,6 +446,7 @@ function FullScreenEditorModule(options) { case 'view' : self.switchToFooter(); + //self.observeViewPosition(); break; } @@ -558,7 +559,7 @@ function FullScreenEditorModule(options) { function helpDisplayed(err, artData) { self.client.waitForKeyPress(function keyPress(ch, key) { self.redrawScreen(); - self.viewControllers.footerEditorMenu.setFocus(true); + self.viewControllers[self.getFooterName()].setFocus(true); }); } ); @@ -636,6 +637,14 @@ function FullScreenEditorModule(options) { }); }; + /* + this.observeViewPosition = function() { + self.viewControllers.body.getView(1).on('edit position', function positionUpdate(pos) { + console.log(pos.percent + ' / ' + pos.below) + }); + }; + */ + this.switchToHeader = function() { self.viewControllers.body.setFocus(false); self.viewControllers.header.switchFocus(2); // to @@ -765,6 +774,10 @@ function FullScreenEditorModule(options) { // MLTEV won't get key events -- we need to handle them all here? // ...up/down, page up/page down... both should go by pages // ...Next/Prev/Etc. here + }, + viewModeMenuHelp : function(formData, extraArgs) { + self.viewControllers.footerView.setFocus(false); + self.displayHelp(); } }; diff --git a/core/menu_util.js b/core/menu_util.js index 087af3b3..9f471fc9 100644 --- a/core/menu_util.js +++ b/core/menu_util.js @@ -201,6 +201,8 @@ function handleAction(client, formData, conf) { var currentModule = client.currentMenuModule; if(_.isFunction(currentModule.menuMethods[actionAsset.asset])) { currentModule.menuMethods[actionAsset.asset](formData, conf.extraArgs); + } else { + client.log.warn( { method : actionAsset.asset }, 'Method does not exist in module'); } } } diff --git a/core/message.js b/core/message.js index b539d91e..f2f14e27 100644 --- a/core/message.js +++ b/core/message.js @@ -244,6 +244,7 @@ Message.prototype.persist = function(cb) { ); }; +// :TODO: Update this to use a FTN module, e.g. ftn.getQuotePrefix(name) Message.prototype.getFTNQuotePrefix = function(source) { source = source || 'fromUserName'; @@ -275,22 +276,29 @@ Message.prototype.getQuoteLines = function(width, options) { var quoteLines = []; var origLines = this.message + .trim() .replace(/\b/g, '') .split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g); - var wrapOpts = { - width : width, - tabHandling : 'expand', - tabWidth : 4, - }; - - var quotePrefix; + var quotePrefix = ''; // we need this init even if blank if(options.includePrefix) { quotePrefix = ' ' + this.getFTNQuotePrefix(options.prefixSource || 'fromUserName') + '> '; } + var wrapOpts = { + width : width - quotePrefix.length, + tabHandling : 'expand', + tabWidth : 4, + }; + + function addPrefix(l) { + return quotePrefix + l; + } + + var wrapped; for(var i = 0; i < origLines.length; ++i) { - Array.prototype.push.apply(quoteLines, wordWrapText(quotePrefix + origLines[i], wrapOpts).wrapped); + wrapped = wordWrapText(origLines[i], wrapOpts).wrapped; + Array.prototype.push.apply(quoteLines, _.map(wrapped, addPrefix)); } return quoteLines; diff --git a/core/multi_line_edit_text_view.js b/core/multi_line_edit_text_view.js index bb89f7e6..d54a453f 100644 --- a/core/multi_line_edit_text_view.js +++ b/core/multi_line_edit_text_view.js @@ -365,7 +365,7 @@ function MultiLineEditTextView(options) { self.updateTextWordWrap(index); self.redrawRows(self.cursorPos.row, self.dimens.height); - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); } else if('delete line' === operation) { // // Delete a visible line. Note that this is *not* the "physical" line, or @@ -401,7 +401,7 @@ function MultiLineEditTextView(options) { if(isLastLine) { self.cursorEndOfPreviousLine(); } else { - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); } } }; @@ -438,7 +438,7 @@ function MultiLineEditTextView(options) { self.cursorPos.col += cursorOffset; self.client.term.rawWrite(ansi.right(cursorOffset)); } else { - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); } } else { // @@ -561,7 +561,7 @@ function MultiLineEditTextView(options) { }; }; - this.moveClientCusorToCursorPos = function() { + this.moveClientCursorToCursorPos = function() { var absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col); self.client.term.rawWrite(ansi.goto(absPos.row, absPos.col)); }; @@ -669,14 +669,14 @@ function MultiLineEditTextView(options) { } else { self.cursorPos.col = 0; } - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); self.emitEditPosition(); }; this.keyPressEnd = function() { self.cursorPos.col = self.getTextEndOfLineColumn(); - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); self.emitEditPosition(); }; @@ -687,7 +687,7 @@ function MultiLineEditTextView(options) { self.adjustCursorIfPastEndOfLine(true); } else { self.cursorPos.row = 0; - self.moveClientCusorToCursorPos(); // :TODO: ajust if eol, etc. + self.moveClientCursorToCursorPos(); // :TODO: ajust if eol, etc. } self.emitEditPosition(); @@ -817,7 +817,7 @@ function MultiLineEditTextView(options) { } if(forceUpdate) { - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); } }; @@ -874,7 +874,7 @@ function MultiLineEditTextView(options) { self.cursorPos = { row : 0, col : 0 }; self.redraw(); - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); }; this.cursorEndOfDocument = function() { @@ -883,7 +883,7 @@ function MultiLineEditTextView(options) { self.cursorPos.col = self.getTextEndOfLineColumn(); self.redraw(); - self.moveClientCusorToCursorPos(); + self.moveClientCursorToCursorPos(); }; this.cursorBeginOfNextLine = function() { @@ -984,7 +984,7 @@ MultiLineEditTextView.prototype.redraw = function() { MultiLineEditTextView.prototype.setFocus = function(focused) { this.client.term.rawWrite(this.getSGRFor('text')); - this.moveClientCusorToCursorPos(); + this.moveClientCursorToCursorPos(); MultiLineEditTextView.super_.prototype.setFocus.call(this, focused); }; @@ -1036,12 +1036,6 @@ var HANDLED_SPECIAL_KEYS = [ 'delete line', ]; -/* -var EDIT_MODE_KEYS = [ - 'line feed', 'insert', 'tab', 'backspace', 'del', 'delete line' -]; -*/ - var PREVIEW_MODE_KEYS = [ 'up', 'down', 'page up', 'page down' ]; @@ -1078,6 +1072,13 @@ MultiLineEditTextView.prototype.getTextEditMode = function() { }; MultiLineEditTextView.prototype.getEditPosition = function() { - return { row : this.getTextLinesIndex(this.cursorPos.row), col : this.cursorPos.col } + var currentIndex = this.getTextLinesIndex() + 1; + + return { + row : this.getTextLinesIndex(this.cursorPos.row), + col : this.cursorPos.col, + percent : Math.floor(((currentIndex / this.textLines.length) * 100)), + below : this.getRemainingLinesBelowRow(), + }; }; diff --git a/core/view_controller.js b/core/view_controller.js index b62f96b5..b6ae89bf 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -42,21 +42,20 @@ function ViewController(options) { // Process key presses treating form submit mapped keys special. // Everything else is forwarded on to the focused View, if any. // - if(key) { - var actionForKey = self.actionKeyMap[key.name] - if(actionForKey) { - if(_.isNumber(actionForKey.viewId)) { - // - // Key works on behalf of a view -- switch focus & submit - // - self.switchFocus(actionForKey.viewId); - self.submitForm(key); - } else if(_.isString(actionForKey.action)) { - menuUtil.handleAction( - self.client, - { key : key }, // formData - actionForKey); // action block - } + var actionForKey = key ? self.actionKeyMap[key.name] : self.actionKeyMap[ch]; + //var actionForKey = self.actionKeyMap[key.name] || self.actionKeyMap[ch]; + if(actionForKey) { + if(_.isNumber(actionForKey.viewId)) { + // + // Key works on behalf of a view -- switch focus & submit + // + self.switchFocus(actionForKey.viewId); + self.submitForm(key); + } else if(_.isString(actionForKey.action)) { + menuUtil.handleAction( + self.client, + { ch : ch, key : key }, // formData + actionForKey); // action block } } @@ -298,6 +297,21 @@ function ViewController(options) { if(!options.detached) { this.attachClientEvents(); } + + this.setViewFocusWithEvents = function(view, focused) { + if(!view || !view.acceptsFocus) { + return; + } + + if(focused) { + self.switchFocusEvent('return', view); + self.focusedView = view; + } else { + self.switchFocusEvent('leave', view); + } + + view.setFocus(focused); + }; } util.inherits(ViewController, events.EventEmitter); @@ -358,23 +372,19 @@ ViewController.prototype.setFocus = function(focused) { } else { this.detachClientEvents(); } + + // :TODO: without this, setFocus(false) is broken (doens't call focus events); with it, FSE menus break!! +// this.setViewFocusWithEvents(this.focusedView, focused); }; ViewController.prototype.switchFocus = function(id) { this.setFocus(true); // ensure events are attached - if(this.focusedView && this.focusedView.acceptsFocus) { - this.switchFocusEvent('leave', this.focusedView); - this.focusedView.setFocus(false); - } + // remove from old + this.setViewFocusWithEvents(this.focusedView, false); - var view = this.getView(id); - if(view && view.acceptsFocus) { - this.switchFocusEvent('return', view); - - this.focusedView = view; - this.focusedView.setFocus(true); - } + // set to new + this.setViewFocusWithEvents(this.getView(id), true); }; ViewController.prototype.nextFocus = function() { @@ -624,7 +634,7 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { callback(null); return; } - + formConfig.actionKeys.forEach(function akEntry(ak) { // // * 'keys' must be present and be an array of key names diff --git a/mods/menu.hjson b/mods/menu.hjson index 446a537b..ddd942f1 100644 --- a/mods/menu.hjson +++ b/mods/menu.hjson @@ -439,7 +439,7 @@ footerEdit: demo_fse_netmail_footer_edit.ans footerEditMenu: demo_fse_netmail_footer_edit_menu.ans footerView: MSGVFTR - help: demo_fse_netmail_help.ans + help: MSGVHLP }, editorMode: view editorType: area @@ -502,34 +502,48 @@ } } }, - "4" : { - "mci" : { - "HM1" : { - // (P)rev/(N)ext/Post/(R)eply/(Q)uit/(?)Help - // (#)Jump/(L)Index (msg list)/Last - "items" : [ "Prev", "Next", "Reply", "Quit", "Help" ] + 4: { + mci: { + HM1: { + // :TODO: (#)Jump/(L)Index (msg list)/Last + items: [ + // (P)revious + Prev + // (N)ext + Next + // (R)reply + Reply + // (Q)uit (ESC) + Quit + // (?)Help + Help + ] } }, "submit" : { "*" : [ { - "value" : { "1" : 0 }, - "action" : "@method:prevMessage" - }, + value: { 1: 0 } + action: @method:prevMessage" + } { - "value" : { "1" : 1 }, - "action" : "@method:nextMessage" - }, + value: { 1: 1 } + action: @method:nextMessage + } { value: { 1: 2 } action: @method:replyMessage extraArgs: { menu: messageAreaReplyPost } - }, + } { - "value" : { "1" : 3 }, - "action" : "@menu:messageArea" + value: { 1: 3 } + action: @menu:messageArea + } + { + value: { 1: 4 } + action: @method:viewModeMenuHelp } ] }, @@ -549,11 +563,6 @@ keys: [ "n", "shift + n" ] action: @method:nextMessage } - { - keys: [ "escape", "q", "shift + q" ] - action: @menu:messageArea - } - // :TODO: why the fuck is 'r' not working but 'n' for example does? { keys: [ "r", "shift + r" ] action: @method:replyMessage @@ -561,6 +570,14 @@ menu: messageAreaReplyPost } } + { + keys: [ "escape", "q", "shift + q" ] + action: @menu:messageArea + } + { + keys: [ "?" ] + action: @method:viewModeMenuHelp + } { "keys" : [ "down arrow", "up arrow", "page up", "page down" ], "action" : "@method:movementKeyPressed" @@ -578,8 +595,7 @@ quote: MSGQUOT footerEditor: MSGEFTR footerEditorMenu: MSGEMFT - // :TODO: fix help - help: demo_fse_netmail_help.ans + help: MSGEHLP } editorMode: edit editorType: area @@ -602,9 +618,10 @@ argName: subject maxLength: 72 submit: true + textOverflow: ... } TL4: { - // :TODO: this is for RE: line + // :TODO: this is for RE: line (NYI) width: 27 textOverflow: ... } @@ -667,10 +684,27 @@ keys: [ "escape" ] action: @method:editModeEscPressed } + { + keys: [ "s", "shift + s" ] + action: @method:replySave + } + { + keys: [ "d", "shift + d" ] + action: @method:replyDiscard + } + { + keys: [ "q", "shift + q" ] + action: @method:editModeMenuQuote + } + { + keys: [ "?" ] + action: @method:editModeMenuHelp + } ] } } + // Quote builder 5: { mci: { MT1: { @@ -713,7 +747,7 @@ body: MSGBODY footerEditor: MSGEFTR footerEditorMenu: MSGEMFT - help: demo_fse_netmail_help.ans + help: MSGEHLP }, editorMode: edit editorType: area diff --git a/mods/msg_area_post_fse.js b/mods/msg_area_post_fse.js index 6a43b6ad..99a9ee67 100644 --- a/mods/msg_area_post_fse.js +++ b/mods/msg_area_post_fse.js @@ -70,6 +70,11 @@ AreaPostFSEModule.prototype.enter = function(client) { AreaPostFSEModule.prototype.validateToUserName = function(un, cb) { var self = this; + if(!un) { + cb(new Error('Username must be supplied!')); + return; + } + if(!self.isLocalEmail()) { cb(null); return; diff --git a/mods/msg_area_view_fse.js b/mods/msg_area_view_fse.js index e209a279..15eace0f 100644 --- a/mods/msg_area_view_fse.js +++ b/mods/msg_area_view_fse.js @@ -60,8 +60,12 @@ function AreaViewFSEModule(options) { case 'down arrow' : bodyView.scrollDocumentUp(); break; case 'up arrow' : bodyView.scrollDocumentDown(); break; case 'page up' : bodyView.keyPressPageUp(); break; - case 'page down' : bodyView.keyPressPageDown(); break; + case 'page down' : bodyView.keyPressPageDown(); break; } + + // :TODO: need to stop down/page down if doing so would push the last + // visible page off the screen at all + }; this.menuMethods.replyMessage = function(formData, extraArgs) { diff --git a/mods/themes/NU-MAYA/MSGEHLP.ANS b/mods/themes/NU-MAYA/MSGEHLP.ANS new file mode 100644 index 0000000000000000000000000000000000000000..121250cae56785d1709cc7fb2299dbc6d06683e2 GIT binary patch literal 3485 zcmeHJ&2HL25GGe7PQ9e1YWETBK#?L&DM3vSw2^2cRY;Yh;G#%gs0^sD1-wp4d6mx3 z+B>^mQ$$+zQXkB&_nY}b4@SnxnYe>gZi_)HQ)xR}L(v*eSRmYfQEv?Xo-GBLEID&m1Cb zEOFqF9iTCJWHXirj!58KZp&qYks;6{wq~5chz*_=@d+0mCzs6050oK%VU6(y%H$T# zle|ctZK-F>%|p`D&@;be)fn!jNH^-hhHVH5fE2M_Z=uCvjX9f(Th2VCO$m}i`Ij(j zW8fay9Xr}?Esmb7NJ=*VYjo_2T>m)GH3`zvgJeFxni-7HLDbMEWvG7m2!GW?3>_sPti)6xyKH*ho8DS^helN(aOH zY?h{K8P+t2_Tt{E9emn6^r$2X+{B5 z#C@U z*m;Hb2U>Mnhz~ELcqel>(QG!G)pi3*=Wi#6$X}M@H%Liopk%iT@ME*XKqWs_l25J~ z==-QPmtn~Na=C_iqvDb$K8R|DBM^=g?$i~WIhcZH0k8+EG!O$W7tLtA!+(Z~evwYe5tSZ$ z-;~~|ZV6p*V>pfai{ZshuOCJ7ZF=JREQ;Sm=kfW=My+;oa$P&E(e>>8+kv`$cfq^j F{tM_#^9cX| literal 0 HcmV?d00001 diff --git a/mods/themes/NU-MAYA/MSGQUOT.ANS b/mods/themes/NU-MAYA/MSGQUOT.ANS index 16d1be42ad8970cf4ca2bdb8af1be77b0efd1b26..f7d62a3d2c1f55347f1c53108ee7280f09a18e48 100644 GIT binary patch literal 516 zcmb`Cy{>{l5QU*7Ar^MhY-2ymdZ8e0QSRiwk`>!+_mbooHItcC94D*n&9jv1bl5XhGg)m_eJ~cy pMEK`>gH^i8CIIYv6$1Z(7{u9i9M_#YJ;zjkHqKcwP3_mS-vL)0nUw$l delta 108 zcmZo+*~Tcct>M$Gh;O8Rt5$J NM#caJuF20CWdZO)Jt+VH diff --git a/mods/themes/NU-MAYA/MSGVHLP.ANS b/mods/themes/NU-MAYA/MSGVHLP.ANS new file mode 100644 index 0000000000000000000000000000000000000000..5fed6314d3a702d79729377a8f571dcfa919e86e GIT binary patch literal 3570 zcmeHJ!EVz)5KXU~uVdse6@Q+V*40yu%3Sc0J ztlGDS$6dLCw+HAdm+cD?Wp=lXb_P+G87eWin!duHII)`lzgg^S^0r7gwL%pUPqbi z!j&Y?lUKIXGx}9R(yVK)T(WwM>LpLMn&1uF5E1|>e7W92i}@0B){kmAJxhZM#6Z=T zFl%jKFBKz2wA~gsnpu&Qb^wLZ@BR_>x4Tf28XegW(=-@|;&dJc;yMa%#G5dl?Q;+E z88JI3=ATXh>+??>Ko9xy95*U22d99JLbNgRuX zTT_#(I!=M?hY=L_#()zI=HW6*7HLgDCYmjtoVu-%<00qJ!F)LEN5ZRMGzs;I zFW+$*(=>6EW)2=br7a(G%K`RUjTZX@IO8;D3po>6QX_>4NeA$RTT%cu`R@_r8@r@TOqj8WytEAC%EV4-`;%Gd{M!_5so-RZ(7bDOg=7UkP48c%|F*OfgDTAnHc(1}cihY%HUSyODb-@4&$Ay)cin|>e zIPN2mYgb~O@LKDDSRPyiWgAEYwoKs{B%^Eg|63}RxKdhIRc!qVEvZ6iz;4Z|Xjt?G RdLF!e(HGaRPt_=Y{{+Xb9s~dY literal 0 HcmV?d00001