Commits (2)
  • Eric Oulashin's avatar
    SlyEdit v1.75: Refactored the quote selection menu and cross-post selection · cd6df2d4
    Eric Oulashin authored
    menus to use DDLightbarMenu instead of SlyEdit's own lightbar code.  This
    allowed the elimination of most of the rest of SlyEdit's own lightbar code,
    reducing the size of SlyEdit.js by about 40Kb.  It also provides some consistency
    in behavior by using a common menu component.
    
    Also, to support some additional required behaviors by SlyEdit, added the following
    'event' functions to the DDLightbarMenu class:
    ValidateSelectItem(pItemRetval): For validating that the user can select an item.
    Takes the item's return value, and returns a bool to indicate whether the item can
    be selected.
    
    OnItemSelect(pItemRetval, pSelected): A function that is called when an item is being
    selected (or de-selected, when multi-select is enabled).  The parameters are the item's
    return value and a boolean to indicate whether the item was selected or de-selected.
    
    Also, added an explicit DoKeyDown() function to DDLightbarMenu to support scrolling
    the menu down when desired (special case for SlyEdit's quote selection window).
    cd6df2d4
  • Rob Swindell's avatar
    Merge branch 'slyedit_175_crosspost_and_quote_menu_refactor_ddlightbarmenu' into 'master' · e47f9c9d
    Rob Swindell authored
    SlyEdit v1.75: Refactored the quote selection menu and cross-post selection
    
    See merge request !128
    e47f9c9d
SlyEdit message editor
Version 1.73
Release date: 2020-03-31
Version 1.75
Release date: 2021-12-11
by
......
This diff is collapsed.
// $Id: SlyEdit_DCTStuff.js,v 1.26 2019/08/15 04:43:33 nightfox Exp $
// $Id: SlyEdit_DCTStuff.js,v 1.21 2019/06/06 03:50:47 nightfox Exp $
/* This file contains DCTEdit-specific functions for SlyEdit.
*
......@@ -13,6 +13,7 @@
* Initial public release
* ... Removed comments ...
* 2019-05-04 Eric Oulashin Updated to use require() instead of load() if possible.
* 2021-12-11 Eric Oulashin Updated the quote window bottom border text
*/
if (typeof(require) === "function")
......@@ -452,10 +453,15 @@ function DrawQuoteWindowBottomBorder_DCTStyle(pEditLeft, pEditRight)
+ HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor
+ "[^Q/ESC] End" + gConfigSettings.DCTColors.QuoteWinBorderColor
+ HORIZONTAL_SINGLE + HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor
+ "[" + UP_ARROW + "/" + DOWN_ARROW + "/PgUp/PgDn/Home/End] Scroll"
+ gConfigSettings.DCTColors.QuoteWinBorderColor + HORIZONTAL_SINGLE
+ HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor
/*
+ "[" + UP_ARROW + "/" + DOWN_ARROW + "/PgUp/PgDn] Scroll"
+ gConfigSettings.DCTColors.QuoteWinBorderColor + HORIZONTAL_SINGLE
+ HORIZONTAL_SINGLE + gConfigSettings.DCTColors.QuoteWinBorderTextColor
+ "[F/L] First/last page";
*/
var helpTextLen = strip_ctrl(quoteHelpText).length;
// Figure out the starting horizontal position on the screen so that
......
// $Id: SlyEdit_IceStuff.js,v 1.33 2019/08/15 04:43:33 nightfox Exp $
// $Id: SlyEdit_IceStuff.js,v 1.29 2019/06/06 03:50:47 nightfox Exp $
/* This contains IceEdit-specific functions for SlyEdit.
*
......@@ -13,6 +13,7 @@
* Initial public release
* ... Removed comments ...
* 2019-05-04 Eric Oulashin Updated to use require() instead of load() if possible.
* 2021-12-11 Eric Oulashin Updated the quote window bottom border text
*/
if (typeof(require) === "function")
......@@ -460,15 +461,19 @@ function DrawQuoteWindowBottomBorder_IceStyle(pEditLeft, pEditRight)
+ gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT
+ gConfigSettings.iceColors.BorderColor1 + HORIZONTAL_DOUBLE
+ gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_LEFT
+ gConfigSettings.iceColors.QuoteWinBorderTextColor + "Up/Down/PgUp/PgDn/Home/End=Scroll"
+ gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT;
/*
+ gConfigSettings.iceColors.QuoteWinBorderTextColor + "Up/Down/PgUp/PgDn=Scroll"
+ gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT
+ gConfigSettings.iceColors.BorderColor1 + HORIZONTAL_DOUBLE
+ gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_LEFT
+ gConfigSettings.iceColors.QuoteWinBorderTextColor + "F/L=First/Last pg"
+ gConfigSettings.iceColors.BorderColor2 + THIN_RECTANGLE_RIGHT;
*/
// The border from here to the end of the line: Random high/low blue
var screenText = "";
for (var posX = pEditLeft + 73; posX <= pEditRight; ++posX)
for (var posX = pEditLeft + 62/*73*/; posX <= pEditRight; ++posX)
screenText += HORIZONTAL_DOUBLE;
screenText += LOWER_RIGHT_VSINGLE_HDOUBLE;
DrawQuoteWindowBottomBorder_IceStyle.border += randomTwoColorString(screenText,
......
// $Id: SlyEdit_Misc.js,v 1.59 2020/03/04 20:59:50 nightfox Exp $
// $Id: SlyEdit_Misc.js,v 1.51 2019/06/06 03:50:47 nightfox Exp $
/* This file declares some general helper functions and variables
* that are used by SlyEdit.
......@@ -42,6 +42,7 @@
* the user can post in a sub-board by checking the can_post
* property of the sub-board rather than checking the
* ARS. The can_post property covers more cases.
* 2021-12-09 Eric Oulashin Added consolePauseWithoutText()
*/
if (typeof(require) === "function")
......@@ -4401,6 +4402,15 @@ function consolePauseWithESCChars(pCfgObj)
getKeyWithESCChars(K_NOSPIN|K_NOCRLF|K_NOECHO, pCfgObj);
}
// Sets the "pause" text to an empty string, does a console.pause(), then restores the pause text.
function consolePauseWithoutText()
{
var originalPausePromptText = bbs.text(Pause); // 563: The "Press a key" text in text.dat
bbs.replace_text(Pause, "");
console.pause();
bbs.revert_text(Pause);
}
// Inputs a keypress from the user and handles some ESC-based
// characters such as PageUp, PageDown, and ESC. If PageUp
// or PageDown are pressed, this function will return the
......
......@@ -229,6 +229,55 @@ lbMenu.GetItem = function(pItemIndex) {
menuItemObj.retval = itemRetval;
return menuItemObj; // The DDLightbarMenu object will use this when displaying the menu
};
If you want to set the currently selected item before calling GetVal() to allow user input,
you should call the SetSelectedItemIdx() function and pass the index to that.
lbMenu.SetSelectedItemIdx(5);
For selecting an item, it may be desirable to validate whether a user should be allowed
to select the item. DDLightbarMenu has a member function it calls, ValidateSelectItem(),
to do just that. It takes the selected item's return value and returns a boolean to signify
whether the user can select it. By default, it just returns true (allowing the user to
select any item). When the user can't choose a value, your code should output why.
To change its behavior, you can overwrite it as follows (assuming lbMenu
is a DDLightbarMenu object):
lbMenu.ValidateSelectItem = function(pItemRetval) {
// Should the user be able to select the item with the return val indicated
// by pItemRetval?
if (yourValidationCode(pItemRetval))
return true;
else
{
console.print("* Can't choose " + pItemRetval + " because blah blah blah!\r\n\1p");
return false;
}
}
OnItemSelect is a function that is called when an item is selected, or toggled
if multi-select is enabled.
Parameters:
pItemRetval: The return value of the item selected
pSelected: Boolean - Whether the item was selected or de-selected. De-selection
is possible when multi-select is enabled.
lbMenu.OnItemSelect = function(pItemRetval, pSelected)
{
// Do something with pItemRetval. pSelected tells whether the item was selected,
// or de-selected if multi-select is enabled.
}
The property exitOnItemSelect specifies whether or not to exit the input loop when an item is
selected/submitted (i.e. with ENTER; not for toggling with multi-select). This is true by
default. It can be desirable to set this to false in some situations, such as when you want a
menu with a custom OnItemSelect() function specified and you want the menu to continue to
be displayed allowing the user to select an item.
lbMenu.exitOnItemSelect = false;
The 'key down' behavior can be called explicitly, if needed, by calling the DoKeyDown() function.
It takes 2 parameters: An object of selected item indexes (as passed to GetVal()) and, optionally,
the pre-calculated number of items.
lbMenu.DoKeyDown(pNumItems, pSelectedItemIndexes);
*/
if (typeof(require) === "function")
......@@ -244,12 +293,12 @@ else
// Keyboard keys
var KEY_ESC = ascii(27);
var KEY_ENTER = "\x0d";
// PageUp & PageDown keys - Synchronet 3.17 as of about December 18, 2017
// use CTRL-P and CTRL-N for PageUp and PageDown, respectively. key_defs.js
// defines them as KEY_PAGEUP and KEY_PAGEDN (key_defs.js is loaded by
// sbbsdefs.js).
//var KEY_ESC = ascii(27);
// Box-drawing/border characters: Single-line
var UPPER_LEFT_SINGLE = "\xDA";
......@@ -374,6 +423,10 @@ function DDLightbarMenu(pX, pY, pWidth, pHeight)
// /^\1[krgybmcw01234567hinpq,;\.dtl<>\[\]asz]$/i
this.syncAttrRegex = /\1[krgybmcw01234567hinpq,;\.dtl<>\[\]asz]/i;
// Whether or not to exit the input loop when an item is selected/submitted
// (i.e. with ENTER; not for toggling with multi-select)
this.exitOnItemSelect = true;
// Things for mouse support
this.mouseTimeout = 0; // Timeout in ms. Currently using 0 for no timeout.
this.mouseEnabled = false; // To pass to mouse_getkey
......@@ -401,6 +454,7 @@ function DDLightbarMenu(pX, pY, pWidth, pHeight)
this.RemoveAllItemHotkeys = DDLightbarMenu_RemoveAllItemHotkeys;
this.GetMouseClickRegion = DDLightbarMenu_GetMouseClickRegion;
this.GetVal = DDLightbarMenu_GetVal;
this.DoKeyDown = DDLightbarMenu_DoKeyDown;
this.SetBorderChars = DDLightbarMenu_SetBorderChars;
this.SetColors = DDLightbarMenu_SetColors;
this.GetNumItemsPerPage = DDLightbarMenu_GetNumItemsPerPage;
......@@ -423,6 +477,21 @@ function DDLightbarMenu(pX, pY, pWidth, pHeight)
this.ItemUsesAltColors = DDLightbarMenu_ItemUsesAltColors;
this.GetColorForItem = DDLightbarMenu_GetColorForItem;
this.GetSelectedColorForItem = DDLightbarMenu_GetSelectedColorForItem;
this.SetSelectedItemIdx = DDLightbarMenu_SetSelectedItemIdx;
// ValidateSelectItem is a function for validating that the user can select an item.
// It takes the selected item's return value and returns a boolean to signify whether
// the user can select it.
this.ValidateSelectItem = function(pItemRetval) { return true; }
// OnItemSelect is a function that is called when an item is selected, or toggled
// if multi-select is enabled.
//
// Parameters:
// pItemRetval: The return value of the item selected
// pSelected: Boolean - Whether the item was selected or de-selected. De-selection
// is possible when multi-select is enabled.
this.OnItemSelect = function(pItemRetval, pSelected) { }
// Set some things based on the parameters passed in
if ((typeof(pX) == "number") && (typeof(pY) == "number"))
......@@ -869,6 +938,11 @@ function DDLightbarMenu_GetItemText(pIdx, pItemLen, pHighlight, pSelected)
itemText = strip_ctrl(menuItem.text);
if (itemTextDisplayableLen(itemText, this.ampersandHotkeysInItems) > itemLen)
itemText = itemText.substr(0, itemLen);
// If the item text is empty, then fill it with spaces for the item length
// so that the line's colors/attributes will be applied for the whole line
// when written
if (strip_ctrl(itemText).length == 0)
itemText = format("%" + itemLen + "s", "");
// Add the item color to the item text
itemText = addAttrsToString(itemText, itemColor);
// If ampersandHotkeysInItems is true, see if there's an ampersand in
......@@ -1228,48 +1302,7 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
}
else if ((this.lastUserInput == KEY_DOWN) || (this.lastUserInput == KEY_RIGHT))
{
if (this.selectedItemIdx < numItems-1)
{
// Draw the current item in regular colors
this.WriteItemAtItsLocation(this.selectedItemIdx, false, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
++this.selectedItemIdx;
// Draw the new current item in selected colors
// If the selected item is below the bottom of the menu, then we'll need to
// scroll the items up.
var numItemsPerPage = (this.borderEnabled ? this.size.height - 2 : this.size.height);
if (this.selectedItemIdx > this.topItemIdx+numItemsPerPage-1)
{
++this.topItemIdx;
this.Draw(selectedItemIndexes);
}
else
{
// The selected item is not below the bottom of the menu, so we can
// just draw the selected item highlighted.
this.WriteItemAtItsLocation(this.selectedItemIdx, true, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
}
}
else
{
// selectedItemIdx is the last item index. If wrap navigation is enabled,
// then go to the first item.
if (this.wrapNavigation)
{
// Draw the current item in regular colors
this.WriteItemAtItsLocation(this.selectedItemIdx, false, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
// Go to the first item and scroll to the top if necessary
this.selectedItemIdx = 0;
var oldTopItemIdx = this.topItemIdx;
this.topItemIdx = 0;
if (this.topItemIdx != oldTopItemIdx)
this.Draw(selectedItemIndexes);
else
{
// Draw the new current item in selected colors
this.WriteItemAtItsLocation(this.selectedItemIdx, true, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
}
}
}
this.DoKeyDown(selectedItemIndexes, numItems);
}
else if (this.lastUserInput == KEY_PAGEUP)
{
......@@ -1446,6 +1479,12 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
}
// Enter key or additional select-item key: Select the item & quit out of the input loop
else if ((this.lastUserInput == KEY_ENTER) || (this.SelectItemKeysIncludes(this.lastUserInput)))
{
// Let the user select the item if ValidateSelectItem() returns true
var allowSelectItem = true;
if (typeof(this.ValidateSelectItem) === "function")
allowSelectItem = this.ValidateSelectItem(this.GetItem(this.selectedItemIdx).retval);
if (allowSelectItem)
{
// If multi-select is enabled and if the user hasn't made any choices,
// then add the current item to the user choices. Otherwise, choose
......@@ -1457,12 +1496,26 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
}
else
retVal = this.GetItem(this.selectedItemIdx).retval;
// Run the OnItemSelect event function
if (typeof(this.OnItemSelect) === "function")
this.OnItemSelect(retVal, true);
// Exit the input loop if this.exitOnItemSelect is set to true
if (this.exitOnItemSelect)
continueOn = false;
}
else if (this.lastUserInput == " ")
}
else if (this.lastUserInput == " ") // Add the current item to multi-select
{
// Select the current item
// Add the current item to multi-select if multi-select is enabled
if (this.multiSelect)
{
// Only let the user select the item if ValidateSelectItem() returns true
var allowSelectItem = true;
if (typeof(this.ValidateSelectItem) === "function")
allowSelectItem = this.ValidateSelectItem(this.GetItem(this.selectedItemIdx).retval);
if (allowSelectItem)
{
var added = false; // Will be true if added or false if deleted
if (selectedItemIndexes.hasOwnProperty(this.selectedItemIdx))
......@@ -1478,6 +1531,14 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
added = true;
}
}
// Run the OnItemSelect event function
if (typeof(this.OnItemSelect) === "function")
{
//this.OnItemSelect = function(pItemRetval, pSelected) { }
this.OnItemSelect(this.GetItem(this.selectedItemIdx).retval, added);
}
// Draw a character next to the item if it's selected, or nothing if it's not selected
var XPos = this.pos.x + this.size.width - 2;
var YPos = this.pos.y+(this.selectedItemIdx-this.topItemIdx);
......@@ -1509,6 +1570,7 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
}
}
}
}
// For numbered mode, if the user enters a number, allow the user to
// choose an item by typing its number.
else if (/[0-9]/.test(this.lastUserInput) && this.numberedMode)
......@@ -1617,6 +1679,61 @@ function DDLightbarMenu_GetVal(pDraw, pSelectedItemIndexes)
return (this.multiSelect ? userChoices : retVal);
}
// Performs the key-down behavior for showing the menu items
//
// Parameters:
// pSelectedItemIndexes: An object containing indexes of selected items. This is
// normally a temporary object created/used in GetVal().
// pNumItems: The pre-calculated number of menu items. If this not given, this
// will be retrieved by calling NumItems().
function DDLightbarMenu_DoKeyDown(pSelectedItemIndexes, pNumItems)
{
var selectedItemIndexes = (typeof(pSelectedItemIndexes) === "object" ? pSelectedItemIndexes : {});
var numItems = (typeof(pNumItems) === "number" ? pNumItems : this.NumItems());
if (this.selectedItemIdx < numItems-1)
{
// Draw the current item in regular colors
this.WriteItemAtItsLocation(this.selectedItemIdx, false, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
++this.selectedItemIdx;
// Draw the new current item in selected colors
// If the selected item is below the bottom of the menu, then we'll need to
// scroll the items up.
var numItemsPerPage = (this.borderEnabled ? this.size.height - 2 : this.size.height);
if (this.selectedItemIdx > this.topItemIdx+numItemsPerPage-1)
{
++this.topItemIdx;
this.Draw(selectedItemIndexes);
}
else
{
// The selected item is not below the bottom of the menu, so we can
// just draw the selected item highlighted.
this.WriteItemAtItsLocation(this.selectedItemIdx, true, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
}
}
else
{
// selectedItemIdx is the last item index. If wrap navigation is enabled,
// then go to the first item.
if (this.wrapNavigation)
{
// Draw the current item in regular colors
this.WriteItemAtItsLocation(this.selectedItemIdx, false, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
// Go to the first item and scroll to the top if necessary
this.selectedItemIdx = 0;
var oldTopItemIdx = this.topItemIdx;
this.topItemIdx = 0;
if (this.topItemIdx != oldTopItemIdx)
this.Draw(selectedItemIndexes);
else
{
// Draw the new current item in selected colors
this.WriteItemAtItsLocation(this.selectedItemIdx, true, selectedItemIndexes.hasOwnProperty(this.selectedItemIdx));
}
}
}
}
// Sets the characters to use for drawing the border. Takes an object specifying
// the values to set, but does not overwrite the whole borderChars object in the
......@@ -2047,12 +2164,35 @@ function DDLightbarMenu_GetColorForItem(pItemIndex, pSelected)
// Return value: Either colors.selectedItemColor or colors.altSelectedItemColor
function DDLightbarMenu_GetSelectedColorForItem(pItemIndex)
{
if (typeof(pItemIndex) !== "number")
return;
if ((pItemIndex < 0) || (pItemIndex >= this.NumItems()))
return "";
return (this.GetItem(pItemIndex).useAltColors ? this.colors.altSelectedItemColor : this.colors.selectedItemColor);
}
// Sets the selected item index for the menu, and sets anything else as appropriate
// (such as the index of the topmost menu item).
//
// Parameters:
// pSelectedItemIdx: The index of the selected item
function DDLightbarMenu_SetSelectedItemIdx(pSelectedItemIdx)
{
if (typeof(pSelectedItemIdx) !== "number")
return;
if ((pSelectedItemIdx < 0) || (pSelectedItemIdx >= this.NumItems()))
return;
this.selectedItemIdx = pSelectedItemIdx;
if (this.selectedItemIdx == 0)
this.topItemIdx = 0;
else if (this.selectedItemIdx >= this.topItemIdx+this.GetNumItemsPerPage())
this.topItemIdx = this.selectedItemIdx - this.GetNumItemsPerPage() + 1;
else if (this.selectedItemIdx < this.topItemIdx)
this.topItemIdx = this.selectedItemIdx;
}
// Calculates the number of solid scrollbar blocks & non-solid scrollbar blocks
// to use. Saves the information in this.scrollbarInfo.numSolidScrollBlocks and
// this.scrollbarInfo.numNonSolidScrollBlocks.
......