From 7a2df5685516c6dc89de8d3858b33fd5d6c1bdb9 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Sat, 27 Jan 2018 22:21:48 -0700 Subject: [PATCH] Menu items can now be arrays of objects * Allows custom members of each item * 'data' overrides selection (vs returning the index) * 'text' is the default member for text if no formatters are supplied * formatters: 'itemFormat' and 'focusItemFormat', e.g. "{member1} - {member2}" --- core/horizontal_menu_view.js | 36 ++++++++++++++++--------- core/menu_view.js | 51 +++++++++++++++++++++++++++++------- core/toggle_menu_view.js | 6 ++--- core/vertical_menu_view.js | 15 ++++++----- 4 files changed, 77 insertions(+), 31 deletions(-) diff --git a/core/horizontal_menu_view.js b/core/horizontal_menu_view.js index 81d477ad..ee1d5318 100644 --- a/core/horizontal_menu_view.js +++ b/core/horizontal_menu_view.js @@ -1,12 +1,14 @@ /* jslint node: true */ 'use strict'; -var MenuView = require('./menu_view.js').MenuView; -var ansi = require('./ansi_term.js'); -var strUtil = require('./string_util.js'); +const MenuView = require('./menu_view.js').MenuView; +const strUtil = require('./string_util.js'); +const formatString = require('./string_format'); +const { pipeToAnsi } = require('./color_codes.js'); +const { goto } = require('./ansi_term.js'); -var assert = require('assert'); -var _ = require('lodash'); +const assert = require('assert'); +const _ = require('lodash'); exports.HorizontalMenuView = HorizontalMenuView; @@ -57,21 +59,29 @@ function HorizontalMenuView(options) { this.drawItem = function(index) { assert(!this.positionCacheExpired); - var item = self.items[index]; + const item = self.items[index]; if(!item) { return; } - var text = strUtil.stylizeString( - item.text, - this.hasFocus && item.focused ? self.focusTextStyle : self.textStyle); + let text; + let sgr; + if(item.focused && self.hasFocusItems()) { + const focusItem = self.focusItems[index]; + text = focusItem ? focusItem.text : item.text; + sgr = ''; + } else if(this.complexItems) { + text = pipeToAnsi(formatString(item.focused ? this.focusItemFormat : this.itemFormat, item)); + sgr = this.focusItemFormat ? '' : (index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()); + } else { + text = strUtil.stylizeString(item.text, item.focused ? self.focusTextStyle : self.textStyle); + sgr = (index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()); + } - var drawWidth = text.length + self.getSpacer().length * 2; // * 2 = sides + const drawWidth = strUtil.renderStringLength(text) + (self.getSpacer().length * 2); self.client.term.write( - ansi.goto(self.position.row, item.col) + - (index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()) + - strUtil.pad(text, drawWidth, self.fillChar, 'center') + `${goto(self.position.row, item.col)}${sgr}${strUtil.pad(text, drawWidth, self.fillChar, 'center')}` ); }; } diff --git a/core/menu_view.js b/core/menu_view.js index 5aed54ca..56899077 100644 --- a/core/menu_view.js +++ b/core/menu_view.js @@ -63,17 +63,37 @@ function MenuView(options) { util.inherits(MenuView, View); MenuView.prototype.setItems = function(items) { - const self = this; + if(Array.isArray(items)) { + // + // Items can be an array of strings or an array of objects. + // + // In the case of objects, items are considered complex and + // may have one or more members that can later be formatted + // against. The default member is 'text'. The member 'data' + // may be overridden to provide a form value other than the + // item's index. + // + // Items can be formatted with 'itemFormat' and 'focusItemFormat' + // + let text; + let stringItem; + this.items = items.map(item => { + stringItem = _.isString(item); + if(stringItem) { + text = item; + } else { + text = item.text || ''; + this.complexItems = true; + } - if(items) { - this.items = []; - items.forEach( itemText => { - this.items.push( - { - text : self.disablePipe ? itemText : pipeToAnsi(itemText, self.client) - } - ); + text = this.disablePipe ? text : pipeToAnsi(text, this.client); + return Object.assign({ }, { text }, stringItem ? {} : item); // ensure we have a text member, plus any others }); + + if(this.complexItems) { + this.itemFormat = this.itemFormat || '{text}'; + this.focusItemFormat = this.focusItemFormat || this.itemFormat; + } } }; @@ -96,12 +116,20 @@ MenuView.prototype.getCount = function() { }; MenuView.prototype.getItems = function() { + if(this.complexItems) { + return this.items; + } + return this.items.map( item => { return item.text; }); }; MenuView.prototype.getItem = function(index) { + if(this.complexItems) { + return this.items[index]; + } + return this.items[index].text; }; @@ -170,6 +198,11 @@ MenuView.prototype.setPropertyValue = function(propName, value) { case 'hotKeySubmit' : this.hotKeySubmit = value; break; case 'justify' : this.justify = value; break; case 'focusItemIndex' : this.focusedItemIndex = value; break; + + case 'itemFormat' : + case 'focusItemFormat' : + this[propName] = value; + break; } MenuView.super_.prototype.setPropertyValue.call(this, propName, value); diff --git a/core/toggle_menu_view.js b/core/toggle_menu_view.js index 27ae2169..39d7ef95 100644 --- a/core/toggle_menu_view.js +++ b/core/toggle_menu_view.js @@ -113,9 +113,9 @@ ToggleMenuView.prototype.getData = function() { }; ToggleMenuView.prototype.setItems = function(items) { + items = items.slice(0, 2); // switch/toggle only works with two elements + 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... string & color, e.g. styleColor1 (same as fillChar color) + this.dimens.width = items.join(' / ').length; // :TODO: allow configurable seperator... string & color, e.g. styleColor1 (same as fillChar color) }; diff --git a/core/vertical_menu_view.js b/core/vertical_menu_view.js index 5b27c36d..e1d6cd17 100644 --- a/core/vertical_menu_view.js +++ b/core/vertical_menu_view.js @@ -5,6 +5,8 @@ const MenuView = require('./menu_view.js').MenuView; const ansi = require('./ansi_term.js'); const strUtil = require('./string_util.js'); +const formatString = require('./string_format'); +const pipeToAnsi = require('./color_codes.js').pipeToAnsi; // deps const util = require('util'); @@ -68,17 +70,16 @@ function VerticalMenuView(options) { const focusItem = self.focusItems[index]; text = focusItem ? focusItem.text : item.text; sgr = ''; + } else if(this.complexItems) { + text = pipeToAnsi(formatString(item.focused ? this.focusItemFormat : this.itemFormat, item)); + sgr = this.focusItemFormat ? '' : (index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()); } else { text = strUtil.stylizeString(item.text, item.focused ? self.focusTextStyle : self.textStyle); sgr = (index === self.focusedItemIndex ? self.getFocusSGR() : self.getSGR()); } - text += self.getSGR(); - self.client.term.write( - ansi.goto(item.row, self.position.col) + - sgr + - strUtil.pad(text, this.dimens.width, this.fillChar, this.justify) + `${ansi.goto(item.row, self.position.col)}${sgr}${strUtil.pad(text, this.dimens.width, this.fillChar, this.justify)}` ); }; } @@ -176,7 +177,9 @@ VerticalMenuView.prototype.onKeyPress = function(ch, key) { }; VerticalMenuView.prototype.getData = function() { - return this.focusedItemIndex; + const item = this.getItem(this.focusedItemIndex); + return item.data ? item.data : this.focusedItemIndex; + //return this.focusedItemIndex; }; VerticalMenuView.prototype.setItems = function(items) {