From 028c5be4182bf0f316f5953dfb42631bf97a661a Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Mon, 27 Apr 2015 16:04:41 -0600 Subject: [PATCH] + ToggleMenuView * Start of hotkeys for menus * General menu cleanup & rework --- core/client.js | 10 ++- core/edit_text_view.js | 7 +- core/mci_view_factory.js | 9 +++ core/menu_view.js | 12 ++++ core/toggle_menu_view.js | 109 +++++++++++++++++++++++++++++++ core/vertical_menu_view.js | 18 ++++- core/view.js | 3 + core/view_controller.js | 5 ++ mods/art/demo_edit_text_view.ans | Bin 588 -> 655 bytes mods/menu.json | 8 ++- 10 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 core/toggle_menu_view.js diff --git a/core/client.js b/core/client.js index df0ed384..bbe768d6 100644 --- a/core/client.js +++ b/core/client.js @@ -17,12 +17,18 @@ exports.Client = Client; //var ANSI_CONTROL_REGEX = /(?:(?:\u001b\[)|\u009b)(?:(?:[0-9]{1,3})?(?:(?:;[0-9]{0,3})*)?[A-M|f-m])|\u001b[A-M]/g; // :TODO: Move all of the key stuff to it's own module + +// +// Resources & Standards: +// * http://www.ansi-bbs.org/ansi-bbs-core-server.html +// var ANSI_KEY_NAME_MAP = { - 0x08 : 'backspace', - 0x09 : 'tab', + 0x08 : 'backspace', // BS + 0x09 : 'tab', // 0x7f : 'del', 0x1b : 'esc', 0x0d : 'enter', + 0x19 : 'end of medium', // EM / CTRL-Y }; var ANSI_KEY_CSI_NAME_MAP = { diff --git a/core/edit_text_view.js b/core/edit_text_view.js index 5a26976f..67ad59e6 100644 --- a/core/edit_text_view.js +++ b/core/edit_text_view.js @@ -34,7 +34,6 @@ EditTextView.prototype.onKeyPress = function(key, isSpecial) { assert(1 === key.length); - // :TODO: how to handle justify left/center? if(this.text.length < this.maxLength) { key = strUtil.stylizeString(key, this.textStyle); @@ -58,8 +57,6 @@ EditTextView.prototype.onKeyPress = function(key, isSpecial) { }; EditTextView.prototype.onSpecialKeyPress = function(keyName) { - // :TODO: handle 'enter' & others for multiLine - if(this.isSpecialKeyMapped('backspace', keyName)) { if(this.text.length > 0) { this.text = this.text.substr(0, this.text.length - 1); @@ -73,6 +70,10 @@ EditTextView.prototype.onSpecialKeyPress = function(keyName) { } } } + } else if(this.isSpecialKeyMapped('clearLine', keyName)) { + this.text = ''; + this.cursorPos.x = 0; + this.setFocus(true); // resetting focus will redraw & adjust cursor } diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index ba761165..aac8c93f 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -6,6 +6,7 @@ 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 Config = require('./config.js').config; var packageJson = require('../package.json'); @@ -177,6 +178,14 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { view = new SpinnerMenuView(options); break; + case 'TM' : + setOption(0, 'textStyle'); + + setFocusOption(0, 'focusTextStyle') + + view = new ToggleMenuView(options); + break; + default : options.text = this.getPredefinedViewLabel(mci.code); if(options.text) { diff --git a/core/menu_view.js b/core/menu_view.js index aab10b87..3f66d675 100644 --- a/core/menu_view.js +++ b/core/menu_view.js @@ -4,8 +4,10 @@ var View = require('./view.js').View; var ansi = require('./ansi_term.js'); var miscUtil = require('./misc_util.js'); + var util = require('util'); var assert = require('assert'); +var _ = require('lodash'); exports.MenuView = MenuView; @@ -23,6 +25,8 @@ function MenuView(options) { this.items = []; } + this.setHotKeys(options.hotkeys); + this.focusedItemIndex = options.focusedItemIndex || 0; this.focusedItemIndex = this.items.length >= this.focusedItemIndex ? this.focusedItemIndex : 0; @@ -35,6 +39,7 @@ function MenuView(options) { this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1); this.justify = options.justify || 'none'; + /* this.moveSelection = function(fromIndex, toIndex) { assert(!self.positionCacheExpired); assert(fromIndex >= 0 && fromIndex <= self.items.length); @@ -47,6 +52,7 @@ function MenuView(options) { self.focusedItemIndex = toIndex; self.drawItem(toIndex); }; + */ /* this.cachePositions = function() { @@ -97,3 +103,9 @@ MenuView.prototype.setItems = function(items) { } }; +MenuView.prototype.setHotKeys = function(hotkeys) { + if(_.isObject(hotkeys)) { + this.hotkeys = hotkeys; + } +} + diff --git a/core/toggle_menu_view.js b/core/toggle_menu_view.js new file mode 100644 index 00000000..f519b8c6 --- /dev/null +++ b/core/toggle_menu_view.js @@ -0,0 +1,109 @@ +/* jslint node: true */ +'use strict'; + +var MenuView = require('./menu_view.js').MenuView; +var ansi = require('./ansi_term.js'); +var strUtil = require('./string_util.js'); + +var util = require('util'); +var assert = require('assert'); +var _ = require('lodash'); + +exports.ToggleMenuView = ToggleMenuView; + +function ToggleMenuView (options) { + options.cursor = options.cursor || 'hide'; + + MenuView.call(this, options); + + var self = this; + + this.cachePositions = function() { + self.positionCacheExpired = false; + }; + + this.updateSelection = function() { + assert(!self.positionCacheExpired); + assert(this.focusedItemIndex >= 0 && this.focusedItemIndex <= self.items.length); + + self.redraw(); + }; +} + +util.inherits(ToggleMenuView, MenuView); + +ToggleMenuView.prototype.redraw = function() { + ToggleMenuView.super_.prototype.redraw.call(this); + + this.cachePositions(); + + this.client.term.write(this.getANSIColor(this.hasFocus ? this.getFocusColor() : this.getColor())); + + assert(this.items.length === 2); + for(var i = 0; i < 2; i++) { + var item = this.items[i]; + var text = strUtil.stylizeString(item.text, i === this.focusedItemIndex ? this.focusTextStyle : this.textStyle); + + if(1 === i) { + this.client.term.write(this.getANSIColor(this.getColor()) + ' / '); // :TODO: We need a color for this!!! + } + + this.client.term.write(this.getANSIColor(i === this.focusedItemIndex ? this.getFocusColor() : this.getColor())); + this.client.term.write(text); + } +} + +ToggleMenuView.prototype.setFocus = function(focused) { + ToggleMenuView.super_.prototype.setFocus.call(this, focused); + + this.redraw(); +}; + +ToggleMenuView.prototype.onKeyPress = function(key, isSpecial) { + if(isSpecial || !this.hotkeys) { + return; + } + + assert(1 === key.length); + + var keyIndex = this.hotkeys[key]; + if(!_.isUndefined(keyIndex)) { + this.focusedItemIndex = keyIndex; + this.updateSelection(); + } + + ToggleMenuView.super_.prototype.onKeyPress.call(this, key, isSpecial); +} + +ToggleMenuView.prototype.onSpecialKeyPress = function(keyName) { + + if(this.isSpecialKeyMapped('right', keyName) || this.isSpecialKeyMapped('down', keyName)) { + if(this.items.length - 1 === this.focusedItemIndex) { + this.focusedItemIndex = 0; + } else { + this.focusedItemIndex++; + } + } else if(this.isSpecialKeyMapped('left', keyName) || this.isSpecialKeyMapped('up', keyName)) { + if(0 === this.focusedItemIndex) { + this.focusedItemIndex = this.items.length - 1; + } else { + this.focusedItemIndex--; + } + } + + this.updateSelection(); + + ToggleMenuView.super_.prototype.onSpecialKeyPress.call(this, keyName); +} + +ToggleMenuView.prototype.getData = function() { + return this.focusedItemIndex; +}; + +ToggleMenuView.prototype.setItems = function(items) { + ToggleMenuView.super_.prototype.setItems.call(this, items); + + this.items = this.items.splice(0, 2); // switch/toggle only works with two elements + + this.dimens.width = this.items.join(' / ').length; // :TODO: allow configurable seperator +} diff --git a/core/vertical_menu_view.js b/core/vertical_menu_view.js index d58a632b..1a87995f 100644 --- a/core/vertical_menu_view.js +++ b/core/vertical_menu_view.js @@ -16,7 +16,7 @@ function VerticalMenuView(options) { var self = this; - this.itemSpacing = 3; + this.itemSpacing = 3; // :TODO: bring from options/configurable this.calculateDimens = function() { if(!self.dimens || !self.dimens.width) { @@ -54,6 +54,19 @@ function VerticalMenuView(options) { } }; + this.changeSelection = function(fromIndex, toIndex) { + assert(!self.positionCacheExpired); + 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.drawItem = function(index) { assert(!this.positionCacheExpired); @@ -91,6 +104,7 @@ VerticalMenuView.prototype.setFocus = function(focused) { this.redraw(); }; + VerticalMenuView.prototype.onSpecialKeyPress = function(keyName) { var prevFocusedItemIndex = this.focusedItemIndex; @@ -110,7 +124,7 @@ VerticalMenuView.prototype.onSpecialKeyPress = function(keyName) { } if(prevFocusedItemIndex !== this.focusedItemIndex) { - this.moveSelection(prevFocusedItemIndex, this.focusedItemIndex); + this.changeSelection(prevFocusedItemIndex, this.focusedItemIndex); } VerticalMenuView.super_.prototype.onSpecialKeyPress.call(this, keyName); diff --git a/core/view.js b/core/view.js index 0db3c4f0..b03c91f4 100644 --- a/core/view.js +++ b/core/view.js @@ -18,6 +18,9 @@ var VIEW_SPECIAL_KEY_MAP_DEFAULT = { next : [ 'tab' ], up : [ 'up arrow' ], down : [ 'down arrow' ], + left : [ 'left arrow' ], + right : [ 'right arrow' ], + clearLine : [ 'end of medium' ], }; function View(options) { diff --git a/core/view_controller.js b/core/view_controller.js index 5c7d08c0..9a1d7385 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -203,6 +203,11 @@ function ViewController(options) { view.textMaskChar = self.client.currentThemeInfo.getPasswordChar(); } + value = getViewProp('hotkeys'); + if(_.isObject(value)) { + view.setHotKeys(value); + } + value = getViewProp('submit'); if(_.isBoolean(value)) { diff --git a/mods/art/demo_edit_text_view.ans b/mods/art/demo_edit_text_view.ans index c0b234cf65cee00d9009da6bc82829c976df39f6..e6f7a2401b4f1e2d3af8bdccfea559a1ff8b9f40 100644 GIT binary patch delta 118 zcmX@Z($Bh~jB#=WW3at~f^@W@wXtb#h;+08h|W(>&q?)7%_|MdOf6S{N*m-_0o4Iz tRYQEuq@zttt&PpW?8!$N6V>>b7#J8C0~pvDz(CZ~$yXuF-4VirlK@eQ89e|1 delta 54 ucmeBYJ;So0jB#=aW9Vc)rf9X(j0_Bni~$Vn3}7Ja>Ex>r=I#jL!ASr