Simple Text Menu
While tinkering last night, I wrote a simple menu library that lets the programmer define a list of text options that can have a key and mousing bindings as well. The library is more a starting point than a end product, but it shows a way how we can wrap a system into library that help us separate that functionality from the main program. The user can tag a function (by name) that is called when any option is clicked or the key binding is pressed.
Example:
So our processing loop looks like this..
[pbcode]
loadfont "verdana",1 , 48
cr$=chr$(10)+chr$(13)
Options$ =" L) Load File [BindKey=38][OnClick=LOAD_FUNCTION]"+cr$
Options$+=" S) Save File [BindKey=31][OnClick=SAVE_FUNCTION]"+cr$
Options$+=" D) Delete File [BindKey=32][OnClick=DELETE_FUNCTION]"+cr$
//
GUI_CREATE_SIMPLE_MENU(Options$)
Do
cls $304050
GUI_UPDATE_SIMPLE_MENU_INPUTSTATE()
GUI_DRAW_SIMPLE_MENU()
action$=GUI_Get_SIMPLE_MENU_CLICKS()
if Len(Action$)
Index=FunctionIndex(Action$)
if Index
CallFunction Action$
endif
endif
sync
loop
Function LOad_Function()
cls 255
print "LOAD FUNCTION"
EndFunction
Function SAVE_Function()
cls $00ff00
Print "SAVE FUNCTION"
EndFunction
Function DELETE_Function()
cls $ff0000
Print "DELETE FUNCTION"
EndFunction
[/pbcode]
COMPLETE EXAMPLE:
[pbcode]
loadfont "verdana",1 , 48
cr$=chr$(10)+chr$(13)
Options$ =" L) Load File [BindKey=38][OnClick=LOAD_FUNCTION]"+cr$
Options$+=" S) Save File [BindKey=31][OnClick=SAVE_FUNCTION]"+cr$
Options$+=" D) Delete File [BindKey=32][OnClick=DELETE_FUNCTION]"+cr$
//
GUI_CREATE_SIMPLE_MENU(Options$)
Do
cls $304050
GUI_UPDATE_SIMPLE_MENU_INPUTSTATE()
GUI_DRAW_SIMPLE_MENU()
action$=GUI_Get_SIMPLE_MENU_CLICKS()
if Len(Action$)
Index=FunctionIndex(Action$)
if Index
CallFunction Action$
endif
endif
sync
loop
Function LOad_Function()
cls 255
print "LOAD FUNCTION"
EndFunction
Function SAVE_Function()
cls $00ff00
Print "SAVE FUNCTION"
EndFunction
Function DELETE_Function()
cls $ff0000
Print "DELETE FUNCTION"
EndFunction
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
//------------------------------------------------------------------
Type tSimpleTextMenuInputState
Status
MX
MY
MB
ScanCode
EndType
Type tSimpleTextMenu
Caption$
KeyBindingScanCode
FunctionToCall$
x1,y1,x2,y2 ; render position of this on screen
MouseOver
EndType
Dim SimpleTextMenu(256) as tSimpleTextmenu
Dim SimpleTextmenuInputState as tSimpleTextMenuInputState
function GUI_CREATE_SIMPLE_MENU(MenuOptions$)
// Redim the Global Memu
Dim SimpleTextMenu(256) as tSimpleTextmenu
//
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
dim Rows$(1024)
Options$=Replace$(MenuOptions$,chr$(10),"")
LinesOfText = SplitToArray(options$,chr$(13),rows$(),0,0)
MenuIndex =1
for lp=0 to LinesOftext-1
s$=Rows$(lp)
// Check for empty line
if len(trim$(S$))>0
Param_KeyBind$ =""
Param_Onclick$ =""
Current_Pos =1
do
// Look for square brackets
Left_pos=instring(s$,"[",Current_pos)
if Left_Pos
Right_pos=instring(s$,"]",Current_pos)
If Right_Pos>Left_Pos
Opcode$=Mid$(s$,Left_pos+1,(Right_Pos-Left_Pos)-1)
Current_Pos=Left_pos
S1$= Left$(s$,Left_pos-1)
S2$= CutLeft$(s$,Right_pos)
s$=s1$+s2$
// get Opcode and Value
Value$ =""
Equals_pos = instring(opcode$,"=")
if Equals_pos
Value$ = CutLeft$(Opcode$,Equals_pos)
Opcode$ = Left$(Opcode$,Equals_pos-1)
endif
Select upper$(trim$(opcode$))
// process embedded opcodes for this menu option
case "ONCLICK"
Param_Onclick$ = value$
case "KEYBIND", "BINDKEY", "KEY"
Param_KEYBIND$ = value$
EndSelect
// reset the next search
Current_pos=Left_pos
else
Current_Pos=Left_pos+1
endif
else
exitdo
endif
loop
// -----------------------------------------------
if Len(s$)
// -----------------------------------------------
SimpleTextMenu(MenuIndex)=New tSimpleTextMenu
SimpleTextMenu(MenuIndex).Caption =s$
SimpleTextMenu(MenuIndex).KeyBindingScanCode =val(Param_KeyBind$)
SimpleTextMenu(MenuIndex).FunctionToCall =Param_OnClick$
MenuIndex ++
endif
endif
next
EndFunction
function GUI_DRAW_SIMPLE_MENU()
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
// Message includes the ONCLICK=
MouseStatus = SimpleTextmenuInputState.Status
Mx = SimpleTextmenuInputState.MX
My = SimpleTextmenuInputState.MY
Mb = SimpleTextmenuInputState.MB
CursorX =GetCursorX()
CursorY =GetCursorY()
TH =GettextHeight("|")
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
T$=SimpleTextMenu(lp).Caption$
CursorX2 = CursorX1+GettextWidth(t$)
CursorY2 = CursorY1+TH
SimpleTextMenu(lp).x1=CursorX1
SimpleTextMenu(lp).X2=CursorX2
SimpleTextMenu(lp).Y1=CursorY1
SimpleTextMenu(lp).Y2=CursorY2
MouseOverState = 0
if MouseStatus
if range(My,CursorY1,CursorY2-1)
inkmode 1+32
c=GetInk()
BoxC 0,CursorY1,getSurfaceWidth(),CursorY2,true,RgbFade(c,50)
inkmode 1
MouseOverState = 1
endif
endif
SimpleTextMenu(lp).MouseOver =MouseOverState
// Check if mouse over
text CursorX1,CursorY1, t$
CursorY1=CursorY2
endif
next
EndFunction
Function GUI_Get_SIMPLE_MENU_CLICKS()
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
MouseStatus = SimpleTextmenuInputState.Status
Mx = SimpleTextmenuInputState.MX
My = SimpleTextmenuInputState.MY
Mb = SimpleTextmenuInputState.MB
if MouseStatus
if MB
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
if SimpleTextMenu(lp).MouseOver
if Mb & 1
Action$ = SimpleTextMenu(lp).FunctionToCall
endif
endif
endif
next
endif
endif
SC = SimpleTextmenuInputState.ScanCode
if SC
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
if SimpleTextMenu(lp).KeyBindingScanCode=SC
Action$ = SimpleTextMenu(lp).FunctionToCall
endif
endif
next
endif
EndFunction Action$
Function GUI_UPDATE_SIMPLE_MENU_INPUTSTATE()
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
SimpleTextmenuInputState.Status = true
SimpleTextmenuInputState.Mx =MouseX()
SimpleTextmenuInputState.MY =MouseY()
SimpleTextmenuInputState.MB =MouseButton()
SimpleTextmenuInputState.ScanCode =ScanCode()
EndFunction
Function GUI_INIT_SIMPLE_MENU_INPUTSTATE()
if GetArrayStatus(SimpleTextmenuInputState())=false
Dim SimpleTextmenuInputState as tSimpleTextMenuInputState
endif
EndFunction
[/pbcode]
Another Simple Menu For PlayBASIC
In this example we're creating a left to right stacking menu with Mouse hover and left clicked selection. The menu returns the NAME of the FUNCTION for you to call. So all you do is Name the processing function that name and dynamically call it via CallFunction. This way you can remove the need to manually detection a click and just build your list of processing functions.
used functions: CallFunction, SplitToArray , Dim, MouseX, MouseY and MouseButton
[pbcode]
loadfont "Courier new",1, 42
Dim Menu$(10)
s$="Load,Save,Delete,End"
Count=SplittoArray(s$,",",menu$())
setfps 30
// -----------------------------------------------------------------
do // MAIN LOOP
// -----------------------------------------------------------------
// Clear the screen to black
cls
// Render the menu and process what was clicked (if any)
Clicked$=Draw_Menu(0,0)
text 50,270,"Clicked:"+Clicked$
if len(Clicked$)
Clicked$=upper$(clicked$)
CallFunction Clicked$
endif
sync
loop Clicked$="MENU_END"
cls
print "Program ended"
Sync
waitkey
// -----------------------------------------------------------------
Function Menu_Load()
// -----------------------------------------------------------------
Text 0,200, "Load was selected"
EndFunction
// -----------------------------------------------------------------
Function Menu_Save()
// -----------------------------------------------------------------
Text 0,200, "Save was selected"
EndFunction
// -----------------------------------------------------------------
// -----------------------------------------------------------------
Function Menu_Delete()
// -----------------------------------------------------------------
Text 0,200, "Delete was selected"
EndFunction
// -----------------------------------------------------------------
// -----------------------------------------------------------------
Function Menu_END()
EndFunction
// -----------------------------------------------------------------
// -----------------------------------------------------------------
Function Draw_Menu(Xpos,Ypos)
// -----------------------------------------------------------------
// -----------------------------------------------------------------
Clicked_Menu$=""
mx=mousex()
my=mousey()
mb=mousebutton()
// Compute used slots and Max menu item width
MaxWidth=0
MaxCount=0
for lp=0 to getarrayelements(Menu$())
m$=Menu$(lp)
if Len(M$)>0
MaxWidth=maxval(MaxWidth,GetTextWidth(m$))
MaxCount=lp
else
exit
endif
next
Dim HoverCol(2)
Dim TextCol(2)
HoverCol(0) =$665544
HoverCol(1) =rgbfade(HoverCol(0),50)
TextCol(0) = -1
TextCol(1) = $ff00ff
MaxWidth+=GetTextWidth("__")
oldink=getink()
For lp =0 to MaxCount
// Get Menu item name
m$=Menu$(lp)
// Compute the height of this text
Ypos2=Ypos+gettextheight(m$)
// If the mouse over this rect ??
HoverStatus =pointinbox(mx,my,xpos,ypos,xpos+maxwidth,ypos2)
// check if the mouse is over this button and mouse
// button is pressed
if HoverStatus and mb & 1
Clicked_Menu$="Menu_"+M$
endif
// draw box / rectangle for this item
boxc xpos,ypos,xpos+maxwidth,ypos2,true,HoverCol(HoverStatus)
// draw our text over the top of it
ink TextCol(HoverStatus)
text Xpos,Ypos,m$
Xpos=Xpos+MaxWidth
next
ink OldInk
EndFunction Clicked_Menu$
[/pbcode]
-- Source Code ON PasteBin (https://pastebin.com/299Lv92p)
Simple Menu V0.03
This is updated simple menu code that's used in the BlitzBASIC 2 PlayBASIC translator (https://www.underwaredesign.com/forums/index.php?topic=4625.0)
[pbcode]
; PROJECT : Convert BlitzBasic To PlayBASIC
; EDITED : 9/05/2022
; ---------------------------------------------------------------------
` *=---------------------------------------------------------------------=*
`
` >> TextMenus V0.03 <<
`
` By: Kevin Picone
`
` Started: (21st,June,2020)
` Last Updated: (28th,April,2022)
`
` (c) copyright 2020/2022 Kevin Picone, All rights reserved
`
` *=---------------------------------------------------------------------=*
`
` This library provides a set of function for rendering and navigating
` through user defined text menus.
`
` Functions:
`
`
` *=---------------------------------------------------------------------=*
constant PB_TEXTMENU_LIBRARY_ExplicitState = PBExplicitState
Explicit true
Type tSimpleTextMenuInputState
Status
MX
MY
MB
MB_CLICKED
ScanCode
// Displacement for mouse position (used for doing windows)
Offset_MX
Offset_MY
EndType
Type tSimpleTextMenu
ObjectName$
Caption$
Display
KeyBindingScanCode
FunctionToCall$
x1,y1,x2,y2 ; render position of this on surface
MouseOver
EndType
// -------------------------------------------------------------
// Declare SimpleTextMenu array as a stub, so we move the handles
// in or out of it to change menus
// -------------------------------------------------------------
MakeArray SimpleTextMenu().tSimpleTextmenu
;Dim SimpleTextMenu(256) as tSimpleTextmenu
Dim SimpleTextmenuInputState as tSimpleTextMenuInputState
//
Dim MenuStack(1024)
global SimpleTextmenu_Allow_Input
function GUI_CREATE_SIMPLE_MENU(MenuOptions$)
// Redim the Global Memu
Dim SimpleTextMenu(256) as tSimpleTextmenu
//
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
dim Rows$(1024)
local Options$=Replace$(MenuOptions$,chr$(10),"")
local LinesOfText = SplitToArray(options$,chr$(13),rows$(),0,0)
local lp,MenuIndex =1
local Param_KeyBind$ ,Param_OnClick$, Param_Display,Param_ObjectName$
local Current_Pos , Left_pos,Right_Pos,Equals_pos
local Value$,Opcode$
for lp=0 to LinesOftext-1
local s$=Rows$(lp)
// Check for empty line
if len(trim$(S$))>0
Param_KeyBind$ =""
Param_Onclick$ =""
Param_Display = true
Param_ObjectName$ =""
Current_Pos =1
do
// Look for square brackets
Left_pos=instring(s$,"[",Current_pos)
if Left_Pos
Right_pos=instring(s$,"]",Current_pos)
If Right_Pos>Left_Pos
Opcode$=Mid$(s$,Left_pos+1,(Right_Pos-Left_Pos)-1)
Current_Pos=Left_pos
local S1$= Left$(s$,Left_pos-1)
local S2$= CutLeft$(s$,Right_pos)
s$=s1$+s2$
// get Opcode and Value
Value$ =""
Equals_pos = instring(opcode$,"=")
if Equals_pos
Value$ = CutLeft$(Opcode$,Equals_pos)
Opcode$ = Left$(Opcode$,Equals_pos-1)
endif
// #print opcode$
// #print Value$
Select upper$(trim$(opcode$))
// process embedded opcodes for this menu option
case "ONCLICK"
Param_Onclick$ = value$
case "KEYBIND", "BINDKEY", "KEY"
Param_KEYBIND$ = value$
case "NODISPLAY"
Param_Display = false
case "OBJECTNAME"
Param_ObjectName$ = upper$(value$)
EndSelect
// reset the next search
Current_pos=Left_pos
else
Current_Pos=Left_pos+1
endif
else
exitdo
endif
loop
// -----------------------------------------------
if Len(s$)
// -----------------------------------------------
SimpleTextMenu(MenuIndex)=New tSimpleTextMenu
SimpleTextMenu(MenuIndex).ObjectName =Param_ObjectName$
SimpleTextMenu(MenuIndex).Caption =s$
SimpleTextMenu(MenuIndex).KeyBindingScanCode =val(Param_KeyBind$)
SimpleTextMenu(MenuIndex).FunctionToCall =Param_OnClick$
SimpleTextMenu(MenuIndex).Display =Param_Display
MenuIndex ++
endif
endif
next
EndFunction
function GUI_DRAW_SIMPLE_MENU()
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
// Message includes the ONCLICK=
Local MouseStatus,Mx,My,Mb
MouseStatus = SimpleTextmenuInputState.Status
Mx = SimpleTextmenuInputState.MX
My = SimpleTextmenuInputState.MY
Mb = SimpleTextmenuInputState.MB
// Adjust Mouse position for any displacement
Mx -=SimpleTextmenuInputState.Offset_MX
My -=SimpleTextmenuInputState.Offset_MY
local CurrentSurface = GetSurface()
local VX1,VY1,VX2,VY2
VX1 = getImageViewportX1(Currentsurface)
VY1 = getImageViewportY1(Currentsurface)
VX2 = getImageViewportX2(Currentsurface)
VY2 = getImageViewportY2(Currentsurface)
local TH,CursorX,CursorX1,CursorX2,CursorY,CursorY1,CursorY2
TH =GettextHeight("|")
CursorX =GetCursorX()
CursorY =GetCursorY()
CursorY1 = CursorY
CursorY2 = CursorY+TH
static TextColoursFlag
if TextColoursFlag=0
Dim TextColours(2)
TextColoursFlag++
endif
TextColours(1)=GetInk()
TextColours(0)=TextColours(1) and $00ffffff or $f0000000
local lp,T$
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
if SimpleTextMenu(lp).Display
T$=SimpleTextMenu(lp).Caption$
CursorX2 = CursorX1+GettextWidth(t$)
CursorY2 = CursorY1+TH
SimpleTextMenu(lp).x1=CursorX1
SimpleTextMenu(lp).X2=CursorX2
SimpleTextMenu(lp).Y1=CursorY1
SimpleTextMenu(lp).Y2=CursorY2
local HoverDisplaymentY=0
local MouseOverState = 0
if range(My,CursorY1,CursorY2-1)
if MouseStatus
local c=GetInk()
; BoxC 0,CursorY1,getSurfaceWidth(),CursorY2,true,RgbFade(c,50)
inkmode 1+32
local c1,c2=0
c1=$333333
c1=RgbFade(c,75)
c2=RgbFade(c,50)
; c1=rgb(210,190,185)
shadebox 0,CursorY1,getSurfaceWidth(),CursorY2,c1,c2,c2,c1
shadebox 0,CursorY2,getSurfaceWidth(),CursorY2+4,c1,c2,0,0
inkmode 1
HoverDisplaymentY=-4
endif
MouseOverState = 1
endif
SimpleTextMenu(lp).MouseOver =MouseOverState
local Xpos =CursorX1+(-5*MouseOverState)
local Ypos =CursorY1+(-5*MouseOverState)
if MouseOverState
ink $10000000
text Xpos+6,Ypos+6, t$
ink $40000000
text Xpos+4,Ypos+4, t$
ink $70000000
text Xpos+2,Ypos+2, t$
endif
// Check if mouse over
Ink TextColours(MouseOverState)
text Xpos,Ypos, t$
CursorY1=CursorY2
endif
endif
next
ink TextColours(1)
EndFunction
function GUI_GET_SIMPLE_MENU_HEIGHT()
local lp,TH =GettextHeight("|")
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
if SimpleTextMenu(lp).Display
Height+=TH
endif
endif
next
EndFunction Height
Function GUI_Get_SIMPLE_MENU_CLICKS()
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
Local MouseStatus,Mx,My,Mb,Mbc
MouseStatus = SimpleTextmenuInputState.Status
Mx = SimpleTextmenuInputState.MX
My = SimpleTextmenuInputState.MY
Mb = SimpleTextmenuInputState.MB
Mbc = SimpleTextmenuInputState.MB_Clicked
if MouseStatus
if MB
local lp,Action$
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
if SimpleTextMenu(lp).MouseOver
if (Mbc & 1) or (Mbc & 2)
Action$ = SimpleTextMenu(lp).FunctionToCall
goto Done
endif
endif
endif
next
endif
endif
local SC = SimpleTextmenuInputState.ScanCode
if SC
for lp=0 to getArrayElements(SimpleTextMenu())
if SimpleTextMenu(lp)
if SimpleTextMenu(lp).KeyBindingScanCode=SC
Action$ = SimpleTextMenu(lp).FunctionToCall
endif
endif
next
endif
Done:
EndFunction Action$
Function GUI_Mouse_Displacement(X,Y)
SimpleTextmenuInputState.Offset_MX=X
SimpleTextmenuInputState.Offset_MY=Y
EndFunction
Function GUI_UPDATE_SIMPLE_MENU_INPUTSTATE()
GUI_INIT_SIMPLE_MENU_INPUTSTATE()
SimpleTextmenuInputState.Status = true
if SimpleTextmenu_Allow_Input = true
SimpleTextmenuInputState.Mx =MouseX()
SimpleTextmenuInputState.MY =MouseY()
else
SimpleTextmenuInputState.Mx = -1
SimpleTextmenuInputState.My = -1
endif
local MB = (MouseButton()*SimpleTextmenu_Allow_Input)
// reset the clicked flag
SimpleTextmenuInputState.MB_CLICKED = false
// Check for a state change ??
if SimpleTextmenuInputState.MB <> MB
if MB
SimpleTextmenuInputState.MB_CLICKED = true
endif
endif
// Raw Mouse Button State
SimpleTextmenuInputState.MB = MB
// Handle RAW SCAN CODES
local SC = ScanCode()
SimpleTextmenuInputState.ScanCode = SC * SimpleTextmenu_Allow_Input
EndFunction
Function GUI_INIT_SIMPLE_MENU_INPUTSTATE()
if GetArrayStatus(SimpleTextmenuInputState())=false
Dim SimpleTextmenuInputState as tSimpleTextMenuInputState
SimpleTextmenuInputState = new tSimpleTextMenuInputState
endif
SimpleTextmenu_Allow_Input = true
EndFunction
Function getMenuStackCount()
Count=MenuStack(0)
EndFunction Count
// ---------------------------------------------------------
// Push the current HANDLE of our global INFO array
// to our STACK
// ---------------------------------------------------------
Function GUI_PushMenu()
if getarraystatus(MenuStack())
// Get to the number of items on our stack
local Offset=MenuStack(0)+1
// move the array handle
#if PlayBASICVersion<165
local Handle=GUI_GRabMenuHandle(SimpleTextMenu().tSimpleTextmenu)
MenuStack(Offset)=Handle
#else
MenuStack(Offset) = SimpleTextMenu()
#endif
#print "Handle:"+str$(MenuStack(Offset))
// clear this handle out once it's been moved out
deletearray SimpleTextMenu()
// Store the stack position
MenuStack(0) = Offset
endif
Endfunction
Function GUI_SimpleTextMenuINput(State)
SimpleTextmenu_Allow_Input = State<>0
EndFunction
// ---------------------------------------------------------
// Push the current HANDLE of our global INFO array
// to our STACK
// ---------------------------------------------------------
Function GUI_PopMenu()
if getarraystatus(MenuStack())
// Get to the number of items on our stack
local Offset=MenuStack(0)
if Offset>0
// release the current array using UNDIM
undim SimpleTextMenu()
// move the array handle
SimpleTextMenu()=MenuStack(Offset)
// Store the stack position after the delete
MenuStack(0) = Offset-1
endif
endif
Endfunction
Function GUI_GRabMenuHandle(Thismenu().tSimpleTextmenu)
EndFunction ThisMenu().tSimpleTextmenu
Explicit PB_TEXTMENU_LIBRARY_ExplicitState
[/pbcode]