From d3a856098d7c79ca36d72c62154053d719b512a1 Mon Sep 17 00:00:00 2001
From: echicken <>
Date: Mon, 30 Sep 2013 17:52:53 +0000
Subject: [PATCH] A pretty crappy demo using frame.js to make a
 cursor-navigable UI with icons. For Lesser Keys re: 'bbs' thread on DoveNet
 Synchronet Sysops.

---
 exec/examples/iconshell/files.bin    |   1 +
 exec/examples/iconshell/games.bin    |   1 +
 exec/examples/iconshell/iconshell.js | 159 +++++++++++++++++++++++++++
 exec/examples/iconshell/msg.bin      |   1 +
 exec/examples/iconshell/readme.txt   |  29 +++++
 5 files changed, 191 insertions(+)
 create mode 100644 exec/examples/iconshell/files.bin
 create mode 100644 exec/examples/iconshell/games.bin
 create mode 100644 exec/examples/iconshell/iconshell.js
 create mode 100644 exec/examples/iconshell/msg.bin
 create mode 100644 exec/examples/iconshell/readme.txt

diff --git a/exec/examples/iconshell/files.bin b/exec/examples/iconshell/files.bin
new file mode 100644
index 0000000000..7ba349e4e3
--- /dev/null
+++ b/exec/examples/iconshell/files.bin
@@ -0,0 +1 @@
+ÛÛÜÜÜÛÛÛÛÛFiles
\ No newline at end of file
diff --git a/exec/examples/iconshell/games.bin b/exec/examples/iconshell/games.bin
new file mode 100644
index 0000000000..8c5715b54b
--- /dev/null
+++ b/exec/examples/iconshell/games.bin
@@ -0,0 +1 @@
+ÜÜÜÜÜßxÛxßxÛÜ|Games
\ No newline at end of file
diff --git a/exec/examples/iconshell/iconshell.js b/exec/examples/iconshell/iconshell.js
new file mode 100644
index 0000000000..414f893287
--- /dev/null
+++ b/exec/examples/iconshell/iconshell.js
@@ -0,0 +1,159 @@
+/*
+	Demo using frame.js to make a cursor-navigable user interface with icons.
+	This is not a command shell (I'd put more thought into the planning and
+	customizability of one) but shows some methods that could be used to create
+	such a shell.
+	Probably requires Synchronet 3.15.
+	echicken@bbs.electronicchicken.com
+	(For Lesser Keys re: 'bbs' thread in Synchronet Sysops on DoveNet, Sep 2013)
+*/
+
+load("sbbsdefs.js");
+load("frame.js");
+
+// If you want to edit the icon .bin files and modify their sizes, update these
+var iconWidth = 5;
+var iconHeight = 3;
+
+// Declare some "global" variables that we'll be using later from within functions
+// (This could lead to a whole conversation about variable scope in JS.)
+var frame; // This will be our parent frame, containing all other frames
+var header; // This will be a frame displaying some info text
+var iconBox; // This will be a frame containing our icons
+var cursor; // This frame will be slightly larger than an icon, and sit in the background
+var output; // This will be a frame that we can push output to
+var activeIcon; // We'll use this to track which icon is currently selected
+var userInput; // We'll use this to store the most recent keypress
+var icons = []; // This is an array; we'll store icon objects in here
+
+// Create an icon object and add it to the icons array
+// "name" is whatever we want, "filename" is the .bin graphic to load for the icon
+var makeIcon = function(name, filename) {
+	icons.push(
+		{	'name' : name,
+			// Frame instantiation: var f = new Frame(x, y, width, height, attributes, parentFrame)
+			'frame' : new Frame(
+				// Don't worry too much about this x, y position calculation for now
+				(icons.length == 0) ? 3 : icons[icons.length - 1].frame.x + (iconWidth * 2),
+				3,
+				iconWidth, // This is the width we specified at the beginning
+				iconHeight, // This is the height we specified at the beginning
+				BG_BLACK|WHITE, // See sbbsdefs.js for colour attributes
+				iconBox // We want iconBox to be the parent frame for all icon frames
+			)
+		}
+	);
+	// So, we've pushed an object into the icons array, and we know that it is
+	// the last element of the array right now.  Therefore we can access it as
+	// icons[icons.length - 1].  If we want to see its name, we can look at:
+	// icons[icons.length - 1].name.  If we want to access its frame, we can act
+	// upon icons[icons.length - 1].frame.
+	icons[icons.length - 1].frame.load(js.exec_dir + filename, iconWidth, iconHeight);
+}
+
+// We'll call this function after receiving certain kinds of user input.
+// Basically just sets the activeIcon (wrapping around as needed) and moves the
+// cursor frame to the required position.
+var navigate = function(direction) {
+	switch(direction) {
+		case "left":
+			// This is a faster way of doing the following:
+			// if(activeIcon == 0)
+			// 	activeIcon = icons.length - 1;
+			// else
+			// 	activeIcon--;
+			activeIcon = (activeIcon == 0) ? icons.length - 1 : activeIcon - 1;
+			break;
+		case "right":
+			activeIcon = (activeIcon == icons.length - 1) ? 0 : activeIcon + 1;
+			break;
+		default:
+			break;
+	}
+	// The "cursor" frame is hard coded to be four columns wider and two rows
+	// taller than an icon.  We want to move it so that its top left corner is
+	// two cells to the left of the icon and one cell above the icon.  The
+	// Frame.moveTo method accepts new coordinates for the frame's top left
+	// corner and then relocates the frame to that position.
+	cursor.moveTo(icons[activeIcon].frame.x - 2, icons[activeIcon].frame.y - 1);
+}
+
+// We'll call this function before all others, in order to set things up
+var init = function() {
+
+	console.clear(); // Clear the terminal window
+	activeIcon = 0; // Set a default "active icon", 0 being the first element in the icons array
+	userInput = ""; // Set userInput to an empty string so that it's not 'undefined'
+
+	// frame will fill the entire terminal window, and has no parent
+	frame = new Frame(1, 1, console.screen_columns, console.screen_rows);
+	// header will be at 1, 1, fill the width of the window, be one row high, and be a child of frame
+	header = new Frame(1, 1, console.screen_columns, 1, BG_BLACK|WHITE, frame);
+	// et cetera
+	iconBox = new Frame(1, 2, console.screen_columns, iconHeight + 2, BG_BLACK|WHITE, frame);
+	cursor = new Frame(1, 2, iconWidth + 4, iconBox.height, BG_CYAN, iconBox);
+	output = new Frame(1, 7, console.screen_columns, 1, BG_BLACK|WHITE, frame);
+
+	// Dump some text into the header frame
+	header.putmsg("Press [enter] to select an item, hit [esc] to exit.");
+
+	// Call that handy makeIcon function we made earlier, to load our icons
+	makeIcon("Messages", "msg.bin");
+	makeIcon("Files", "files.bin");
+	makeIcon("Games", "games.bin");
+
+	// Open frame and all of its children
+	frame.open();
+	// Frame.cycle ensures that the frames current contents are displayed in the user's terminal
+	frame.cycle();
+
+}
+
+// This will be our main loop, accepting and reacting to user input
+var main = function() {
+
+	// While the user has not hit escape ...
+	while(ascii(userInput) != 27) {
+
+		// Call frame.cycle once each loop; don't worry about console.gotoxy for now
+		if(frame.cycle())
+			console.gotoxy(console.screen_columns, console.screen_rows);
+
+		// Wait for a keypress from the user, store it in userInput when it arrives
+		var userInput = console.getkey();
+
+		// Evaluate the input we received from the user
+		switch(userInput) {
+			// If they hit the left arrow, call our navigate function with the appropriate argument
+			case KEY_LEFT:
+				navigate("left");
+				break;
+			// "" for a right arrow
+			case KEY_RIGHT:
+				navigate("right");
+				break;
+			// If they hit enter, just say which icon they selected
+			case "\r":
+				output.clear();
+				output.putmsg("Selected: " + icons[activeIcon].name);
+				break;
+			// If input was none of the above, do nothing
+			default:
+				break;
+		}
+
+	}
+
+}
+
+// We'll call this when the script exits, just to tidy up
+var cleanUp = function() {
+
+	frame.close(); // Close the frame
+	console.clear(); // Clear the terminal
+
+}
+
+init(); // Initialize
+main(); // Run the main loop
+cleanUp(); // Tidy up
\ No newline at end of file
diff --git a/exec/examples/iconshell/msg.bin b/exec/examples/iconshell/msg.bin
new file mode 100644
index 0000000000..b3d9954200
--- /dev/null
+++ b/exec/examples/iconshell/msg.bin
@@ -0,0 +1 @@
+ÜßÛßÜÛÛÜÛÛ Msg 
\ No newline at end of file
diff --git a/exec/examples/iconshell/readme.txt b/exec/examples/iconshell/readme.txt
new file mode 100644
index 0000000000..58294ca641
--- /dev/null
+++ b/exec/examples/iconshell/readme.txt
@@ -0,0 +1,29 @@
+Installation:
+
+Set up an external program as follows:
+
++[¦][?]----------------------------------------------------+
+¦                        Icon Shell                        ¦
+¦----------------------------------------------------------¦
+¦ ¦Name                       Icon Shell                   ¦
+¦ ¦Internal Code              ICONSHEL                     ¦
+¦ ¦Start-up Directory         /sbbs/exec/examples/iconshell¦
+¦ ¦Command Line               ?iconshell.js                ¦
+¦ ¦Clean-up Command Line                                   ¦
+¦ ¦Execution Cost             None                         ¦
+¦ ¦Access Requirements                                     ¦
+¦ ¦Execution Requirements                                  ¦
+¦ ¦Multiple Concurrent Users  Yes                          ¦
+¦ ¦Intercept I/O              No                           ¦
+¦ ¦Native Executable          No                           ¦
+¦ ¦Use Shell to Execute       No                           ¦
+¦ ¦Modify User Data           No                           ¦
+¦ ¦Execute on Event           No                           ¦
+¦ ¦Pause After Execution      No                           ¦
+¦ ¦BBS Drop File Type         None                         ¦
+¦?¦Place Drop File In         Node Directory               ¦
++----------------------------------------------------------+
+
+Usage:
+
+Run that external program. :|
\ No newline at end of file
-- 
GitLab