Chapter 10 - Menu's

A menu allows navigating through available options to select a preferred choice.
For simplicity, these examples will use an Annex listbox$ as a quick and easy means of navigation and selection.
Listbox$ takes a comma-separated list of options and returns the selected choice in a previously declared listbox 'variable' (which we will call choice$).
And quite conveniently for our purposes, its comma separated list of options can also be supplied in the form of a text variable rather than being embedded.

When a selection is made from a listbox$ it triggers an OnHtmlChange branch, allowing for a 'refresh' to sync the browsers websocket selection with the variable.
Once the required selection is refreshed into the variable it can be actioned in whatever way we wish... but first, it is all about the branching.

We could do a sequence of SELECT CASE tests and branch as appropriate, or similarly with a sequence of IF THEN tests... but both are rigid and clunky structures which result in more than double or triple the lines of code than there are actual menu options.
The code could be reduced to a single line by using an undocumented ELSE IF capability, eg:
    IF choice$="one" THEN gosub one ELSE IF choice$="two" THEN gosub two ELSE IF choice$="three" THEN gosub three ELSE gosub unknown
This technique is like hammers and multi-statement lines - useful... but not foolproof. So if you don't appreciate its concise elegance, or you blame your tools for self-inflicted injuries, just do things safely by the book without needing to impose similar limits on everyone.
The book (Online Help) has an even more concise solution hiding in plain sight - the gosub variable$ feature - which allows the destination branch name to be supplied in the variable, and therefore doesn't need any sequences of conditional 'tests'... it simply branches to the specified label name contained in the variable.

This is the method used In the example below - any listbox$ selection will trigger an onhtmlchange event causing a jump to a subroutine called chosen:, where a refresh will sync the browser selection into our choice$ variable, then we simply issue gosub choice$ to branch to the selected subroutine label name.

Basic:
' Simple listbox internal menu
cls
options$ = "first,second,third,fourth" 
  'comma-delimited list of menu options which correspond to same-name subroutine branches
choice$ = ""                                         'variable to hold the menu selection
message$ = ""                                     'empty variable for displaying selection confirmation message
a$ = textbox$(message$) + "<br><br>"
a$ = a$ + listbox$(choice$,options$) + "<br><br>"
html a$
onhtmlchange chosen
wait

chosen:
refresh
gosub choice$
return

first:
message$ = "First was chosen"
refresh
return

second:
message$ = "Chose Second this time"
refresh
return

third:
message$ = "Not first or second..."
refresh
return

fourth:
message$ = "Oh no, Last again"
refresh
return


'----------- End ------------


 
The script example above displays the listbox shown on the right which has an initially blank textbox above it.
Selecting a listbox option causes an onhtmlchange branch to chosen:, which issues a gosub to the selected label name.

The selected branch merely selects different text which it refreshes to the browser textbox as visible proof of the selection.
Obviously in practice the selected branch could do anything it is coded for.


In the example, the menu options are supplied in a comma-delimited option$ variable rather than be hard-coded into the listbox.

Another possibility is to load them from external text file (eg: "Menu.txt"), which is easy to create and save and view from the Editor.
This might be useful for offering the same options to different scripts, perhaps for choosing background colours etc.


The menu example above branches to execute code in internal subroutines, but it is even easier to create a menu which loads and executes external scripts.

In the following example a listbox selection still triggers a branch to 'chosen', but instead of using 'gosub chosen$' to branch internally, it uses 'BAS.LOAD chosen$' to run an external file. You need to ensure that those external files do actually exist of course, but for this example demonstration I have made things easy for you by including an extra line in the script which tests if the required files do already exist, and if not, branches to a makefiles subroutine which will create them.
So don't forget to remove "/file1.bas" to "/file4.bas" later.

Basic:
' Simple external file menu
if file.exists("/file4.bas") = 0 then gosub makefiles      'only added for user convenience, is not necessary for the menu
cls
options$ = "/file1.bas,/file2.bas,/file3.bas,/file4.bas"   'comma-delimited list of menu options which correspond to exterrnal filenames
choice$ = ""                                                                 'variable to hold the menu selection
message$ = "Main menu"                                           'the current menu file
a$ = textbox$(message$) + "<br><br>"
a$ = a$ + listbox$(choice$,options$) + "<br><br>"
html a$
onhtmlchange chosen
wait

chosen:
refresh
if
bas.load choice$ <> 0 then message$ = "Unable to load selected file"   
refresh
return

makefiles:  'only added for user convenience, is not necessary for the menu
for c = 1 to 4
 filename$ = "/file" + str$(c) + ".bas"                  'how to expand a single word into a fully qualified filename
 b$ = |cls| + chr$(10)
 b$ = b$ + |options$ = "/file1.bas,/file2.bas,/file3.bas,/file4.bas"| + chr$(10)  
 b$ = b$ + |choice$ = ""| + chr$(10)     
 b$ = b$ + |message$ = "This is " + bas.filename$ | + chr$(10)
 b$ = b$ + |a$ = textbox$(message$) + "<br><br>"| + chr$(10)
 b$ = b$ + |a$ = a$ + listbox$(choice$,options$) + "<br><br>"| + chr$(10)
 b$ = b$ + |html a$| + chr$(10)
 b$ = b$ + |onhtmlchange chosen| + chr$(10)
 b$ = b$ + |wait| + chr$(10)
 b$ = b$ + |chosen:| + chr$(10)
 b$ = b$ + |refresh| + chr$(10)
 b$ = b$ + |if bas.load choice$<>0 then message$="Unable to load selected file"| + chr$(10)
 b$ = b$ + |refresh| + chr$(10)
 b$ = b$ + |return| + chr$(10)
 file.save filename$, b$
 pause 200
next c
return


'----------- End ------------


All the menu option$ filenames consisted of fully-qualified root-relative /path/filename.ext
This is necessary because all files are referenced to the root "/" rather than the current directory (folder).
The addition of the excellent bas,load feature makes it an issue to be aware of, and also know how to deal with it.
To show the issue, save and run the following script into the "/program/" folder (call it something like /program/test.bas)
  filename$ = "findme.bas"
  content$ = "some content"
  file.save filename$, content$
  a$ = "file not found"
  pause 100
  a$ = file.read$(filename$)
  wlog a$

Notice that although filename$ contains only the filename without any path, it was saved in the root "/", not the current folder (check where it is using File Manager).
And similarly if trying to read a file by filename alone without a path, it will be looked for in the "/" root, so will not be found from inside any current folder.
This has important implications for eg: a menu intended to read and display files that exist in the current folder to run a selected file... because it cannot be done by filename alone. All files, wherever they are, must be prefixed by the full root-relative path to their current folder... and to discover the current folder you must use bas.filename$ and extract the path from it (or extract the filename to leave just the path) which can be done something like this (there may be better ways)....
  a$ = bas.filename$
  wlog a$
  path = word.count(a$,"/")
  filename$ = word$(a$,path,"/")
  path$ = replace$(a$,filename$,"")
  wlog filename$
  wlog path$

So the file menu example above used full path+filename+ext only for for simplicity, and saved them into root to ensure they work whatever the current folder.
But that approach has the limitation of hard-coding everything to one folder which is restrictive.
It also imposes longer names to be displayed in the menu, which may not matter too much in a web-based display, but can make them much harder to read on eg: a small OLED display, either because the crucial right-hand filename has been truncated, or because of having to use a much smaller font .

A more practical and satisfying solution would extract just the file names to display in the menu, then recombine the selected name with path and extension, eg:
  filename$ = path$ + choice$ + ext$

The individual name and extension components are easily obtained after the previous path/filename snippet using eg:...
  name$ = word$(filename$,1,".")
  ext$ = "." + word$(filename$,2,".")     '(the dot separator needs to be replaced after being ignored and stripped out as the chopping position character)
 


These menu scripts are only intended as demonstration examples, but this preview video shows an OLED File Launcher Menu and App suite which has since been published.