Code archives/BlitzPlus Gui/Popup menu

This code has been declared by its author to be Public Domain code.

Download source code

Popup menu by Nebula
(Posted 19 years ago)
Right mouse to activate pop up menu.
;
; Blitz + Native Popup menu, No dll or userlib required
;
; (c) R.v.Etten / Nebula in 2004

;
; Currently minor visuals are added and the submenus only go one level deep. The menu's might also sometimes be slightly
; jitterisch.
;
; Functions Draw... can be adjusted to modify the visual apearance of the popup menu

;Todo :
;
; Display key Shotcuts
; Modularize popup control structure
; Put popup collision in timer
; Large menu scrolling (scrollbar?)
; Auto adjust popup layout to size (screen)
; Blanked out text (inactive / disable / enable)
; More user level control function
; More sub menu levels
; Update popup output handling to contents
; Better interactive control
; Inline images and user controls
; Easy user level color control
;
;Todo maybe :
;
; Menu resizing animation
; Skins and oval/round popup menus
; Sound effects
; Fullscreen version
; Menu mode
; Inline animations
; Load data from Ini file

Global winwidth = 640
Global winheight = 480

Global win = CreateWindow("Window",ClientWidth(Desktop())/2-winwidth/2,ClientHeight(Desktop())/2-winheight/2,winwidth,winheight,Desktop(),1+8)

Global popupfontwidth = 8
Global popupfontheight = 13
Global popupclose
Const numpopupmenus = 4
Const numpopupitems = 99
Dim popupdata$(numpopupmenus,numpopupitems,22)
Dim popuppoint(1) ; win,canvas switch
Const popupactive = 0
Const popuptext = 1
Const popupwidth = 2
Const popupheight = 3
Const popupx = 4
Const popupy = 5
Const popupnumitems = 6
Const popuppointer = 7 ; point to another popup menu
Const popupenabled = 8
Const popupline = 9
Const popupmouseontop = 10
Const popupoutdata = 11
Const popupisinitiated = 12 ; goes in x,0,><
Const popupmenuwidth = 13 ; goes in x,0,><
Const popupminwidth = 14 ; goes in x,0,><
Const popuplineactive = 15 ; line?

Global popuplinedr = 55 ; popup dark line
Global popuplinedg = 55
Global popuplinedb = 55
Global popuplinelr = 200 ; popup light line
Global popuplinelg = 200
Global popuplinelb = 200

Global popupcolorr = 150 ; popup surface color
Global popupcolorg = 150
Global popupcolorb = 150
Global popuprectlr = 200 ; light popup rectangle color
Global popuprectlg = 200
Global popuprectlb = 200
Global popuprectdr = 80 ; dark popup rectange color
Global popuprectdg = 80
Global popuprectdb = 80

; Setup popup and sublevels
insertpopupitem(0,0,"Load",1)
insertpopupitem(0,1,"Save",2)
insertpopupline(0,2,3) ; Insert line
insertpopupitem(0,3,"Cut",4)
insertpopupitem(0,4,"Copy",5)
insertpopupitem(0,5,"Paste",6)
insertpopupitem(0,6,"Sub",7)
insertpopupitem(1,0,"Yeah",8)
insertpopupitem(1,1,"this",9)
insertpopupitem(1,2,"is a",10)
insertpopupitem(1,3,"submenu",11)
linkitemtosubmenu(0,4,1) ; menu 0 item 4 gets linked to open menu 1
insertpopupitem(2,0,"Yeah",12)
insertpopupitem(2,1,"this",13)
insertpopupitem(2,2,"is a",14)
insertpopupitem(2,3,"submenu",15)
linkitemtosubmenu(0,6,2) ; menu 0 item 6 gets linked to open menu 2

;
main ; popup is called from this function - one call only
End

Function main()

While we<>$803
we = WaitEvent()
Select we
	Case $101 	;- Key down 
	Case $102 	;- Key up
	If EventData() = 1 Then Exit
	Case $103 	;- Key stroke 
	Case $201 	;- Mouse down 
	Case $202 	;- Mouse up
		If EventData() = 2 Then
			q = popupwindow(num,MouseX(),MouseY(),win)
			If q>0 Then RuntimeError q 
		End If
	Case $203 	;- Mouse move 
	Case $204 	;- Mouse wheel 
	Case $205 	;- Mouse enter 
	Case $206 	;- Mouse leave 
	Case $401 	;- Gadget action 
	Case $801 	;- Window move	
	Case $802 	;- Window size 
	Case $803 	;- Window close 
	Case $804 	;- Window activate 
	Case $1001 	;- Menu event 
	Case $2001 	;- App suspend	
	Case $2002 	;- App resume		
	Case $2003 	;- App Display Change 
	Case $2004 ;- App Begin Modal 
	Case $2005 ;- App End Modal 
	Case $4001	;- Timer tick
End Select
Wend
End Function

Function popupwindow(num,x,y,parent)
	Local win = CreateWindow("",x,y,popupmaxwidth(num),popupmaxheight(num),parent,32)
	Local can = CreateCanvas(0,0,popupmaxwidth(num),popupmaxheight(num),win)
	drawpopup(num,can)
	
	popupclose = False

	Local subwin[5]
	Local subcanvas[5]
	Local subitem[5] ; sub level
	Local subitemmenu[5]
	Local subitemnum[5]
	Local subitemoffsetx[5]
	Local subitemoffsety[5]
	Local subitemactive[5]
	Local subitemorigin[5]

	Local subremovetimer = 0
	Local removesubs = False
	
	olditem = -1
	timer = CreateTimer(40)
	While we<>$803
		we = WaitEvent()
		Select we
			Case $101 	;- Key down 
			Case $102 	;- Key up
			If EventData() = 1 Then Exit
			Case $103 	;- Key stroke 
			Case $201 	;- Mouse down			
				If currentitem = -1 And subitemactive[0] = False Then we=$803
			Case $202 	;- Mouse up
				If subitemactive[0] = True And subitemnum[0] >-1 Then
					If removesubs = False Then
						FreeGadget subwin[0]
						FreeGadget win					
						Return popupoutdata(subitem[0],subitemnum[0])
					End If
				End If
				If subitemactive[0] = False Then
					Return popupoutdata(num,currentitem)
				End If
			Case $203 	;- Mouse move
			currentitem =  popupcollision(num,x,y)
			If subitemactive[0] = True Then subitemnum[0] = popupcollision(subitem[0],subitemoffsetx[0],subitemoffsety[0])
			If subitemactive[0] = True Then drawpopup(subitem[0],subcanvas[0]) :popupsetitemmousestate(num,subitemorigin[0],True) :drawpopup(num,can)
			If subitemactive[0] = True And currentitem <> currentsub And subitemnum[0] = -1 Then currentitem = -1		

			DebugLog "Num : " + num + " : " + currentitem + " : " + subitemnum[0]
			;If  currentitem <> subitemorigin[0] And subitemnum[0] = 0 And subitemactive[0] = True Then
			If subitemnum[0] = -1 And subitemactive[0] = True  Then			
				If  (Not currentitem = subitemorigin[0])
						If removesubs = False Then
							subremovetimer = MilliSecs() + 400
							removesubs = True
							subitemactive[0] = False
						End If
				End If
			End If
			;
			If currentitem >-1
				If olditem <> currentitem Then
					If subitemactive[0] = False Then drawpopup(num,can)
					ex = drawpopupsubitem(num,currentitem,can)
					If ex>-1 And ex<> num Then
						currentsub = currentitem
						If subitemactive[0] = False Then
							If removesubs = True Then
								FreeGadget subwin[0]
							End If
							removesubs = False
							subwin[0] = subpopupwindow(ex,x+popupmaxwidth(num),y+currentitem*popupfontheight,win)
							subcanvas[0] = popuppoint(1)							
							subitem[0] = ex
							;subitemmenu[0]
							subitemoffsetx[0] = x+popupmaxwidth(num)
							subitemoffsety[0] = y+currentitem*popupfontheight
							subitemactive[0] = True
							subitemorigin[0] = currentitem
							subitemnum[0] = 0 ; which level
							olditem = currentitem
						End If
					End If
				End If
			End If
			Case $204 	;- Mouse wheel 
			Case $205 	;- Mouse enter
			currentitem=0
						
			Case $206 	;- Mouse leave
				popupmouseleave(num)
				drawpopup(num,can)
				currentitem = -1
			Case $401 	;- Gadget action 
			Case $801 	;- Window move		
				popupclosegroup()
			Case $802 	;- Window size 
			Case $803 	;- Window close 
			Case $804 	;- Window activate
				popupclosegroup()		
			Case $1001 	;- Menu event
			Case $2001 	;- App suspend	
			Case $2002 	;- App resume		
			Case $2003 	;- App Display Change 
			Case $2004 ;- App Begin Modal 
			Case $2005 ;- App End Modal 
			Case $4001	;- Timer tick
			If popupclose = True Then we=$803
			
			If subremovetimer < MilliSecs() And removesubs = True Then				
				FreeGadget subwin[0] : subitemactive[0] = False : olditem = -1
				removesubs = False
			End If

		End Select
	Wend
	FreeGadget win
End Function

Function linkitemtosubmenu(parent,id,child) ; menu , item, >open menu<
	popupdata(parent,id,popuppointer) = child
End Function
Function insertpopupitem(num,id,newtext$,outid,child=-1)
	popupdata(num,id,popupactive) = True
	popupdata(num,id,popupisinitiated)  = True
	popupdata(num,id,popupminwidth) = 64
	popupdata(num,id,popuptext) = newtext$
	popupdata(num,id,popuppointer) = -1
	popupData(num,id,popupoutdata) = outid
	popupdata(num,id,popuplineactive) = False
	If child <> -1 Then
		popupdata(num,id,popuppointer)  = child ; open another popup menu
	End If
End Function
Function insertpopupline(num,id,outid)
	popupdata(num,id,popupactive) = True
	popupdata(num,id,popupisinitiated)  = True
	popupdata(num,id,popupminwidth) = 64
	popupdata(num,id,popuptext) = " "
	popupdata(num,id,popuppointer) = -1
	popupData(num,id,popupoutdata) = outid
	popupdata(num,id,popuplineactive) = True
End Function

Function deletepopupitem(num,id)
	popupdata(num,id,popupactive) = False
End Function

Function subpopupwindow(num,x,y,parent)
	Local win = CreateWindow("",x,y,popupmaxwidth(num),popupmaxheight(num),parent,32)
	Local can = CreateCanvas(0,0,popupmaxwidth(num),popupmaxheight(num),win)
	drawpopup(num,can)
	popuppoint(0) = win
	popuppoint(1) = can
	Return win
End Function
Function getpopupnumitems(num)
	If popupisinitiated(num) = False Then Return
	cnt=0
	For i=0 To numpopupitems
		If popupactive(num,i) = True Then cnt=cnt+1
	Next
	Return cnt
End Function
Function getpopuptext$(num,id) ; get
	If popupdata(num,id,popupactive) = True Then
		Return popupdata(num,id,popuptext)
	End If
End Function
Function popuptext(num,id,newtext$) ; set
	popupdata(num,id,popuptext) = newtext$
End Function

Function popupactive(num,id)
	; returns if a popup menu item is active
	If popupdata(num,id,popupisinitiated) = True Then Return True
	Return False
End Function
Function popuphassubmenu(num,id)
	If popupactive(num,id) = False Then Return False
	If popupdata(num,id,popuppointer) > -1 Then Return True
	Return False
End Function
Function popupitemisline(num,id)
	If popupactive(num,id) = False Then Return
	If popupdata(num,id,popuplineactive) = True Then Return True
End Function
Function popupcollision(num,x,y)
	w = popupmaxwidth(num)
	h = popupmaxheight(num)
	n = getpopupnumitems(num)
	y=y+10
	If RectsOverlap(MouseX(),MouseY(),1,1,x,y,w-1,h-1) = True Then
		x2 = MouseX()-x
		y2 = MouseY()-y
		y3 = y2/popupfontheight
		popupsetitemmousestate(num,y3,True)
		Return y3
	End If
	Return -1
End Function
Function popupmouseleave(num)
	popupsetitemmousestate(num,0,False)
End Function
Function popupsetitemmousestate(num,id,st)
	If id<0 Then Return
	;erase prev ; optimize!
	For i=0 To numpopupitems
		popupdata(num,i,popupmouseontop) = False
	Next
	;
	popupdata(num,id,popupmouseontop) = st
End Function
Function popupmouseitemcollision(num,id)
	If Int(popupdata(num,id,popupmouseontop)) = True Then Return True
	Return False
End Function
Function popupisinitiated(num)
	If popupdata(num,0,popupisinitiated) = True Then Return True
End Function
Function popupclosegroup()
	popupclose = True
End Function
Function popupoutdata(num,id)
	Return popupdata(num,id,popupoutdata)
End Function

Function drawpopup(num,can) ; visuals
	SetBuffer CanvasBuffer(can)
	ClsColor popupcolorr,popupcolorg,popupcolorb	
	Cls	
	x=0
	y=0
	drawpopuprect(x,y,popupmaxwidth(num),popupmaxheight(num))
	fh = FontHeight()
	For i=0 To numpopupitems
		If popupdata(num,i,popupactive) = True Then
			If popupitemisline(num,i) = True Then
				drawpopupln(x,y,popupmaxwidth(num))
			Else
				drawpopupline(num,x,y,popupdata(num,i,popuptext),popupmouseitemcollision(num,i)) ; draw line non selected
				If popuphassubmenu(num,i) = True Then drawpopuparrow(x+popupmaxwidth(num)-10,y)
			End If
			y=y+fh
		End If
	Next
	FlipCanvas(can)
End Function
Function drawpopuprect(x,y,w,h)
	;	Color 0,0,0 ; Oldskool
	;	Rect x,y,w,h,False
	;	Color 200,200,200
	;	x=x+1 : w=w-2
	;	y=y+1 : h=h-2
	;	Rect x,y,w,h,False
	
	Color popuprectdr,popuprectdg,popuprectdb  ; top to right, top right to bottom (outer)
	Line x,y,x+w,y
	Line x,y,x,y+h
	Color 200,200,200 ; top to right, top right to bottom (inner)
	x=x+1 : w=w-2 : y=y+1 : h=h-2
	Line x,y,x+w,y
	Line x,y,x,y+h
	Color 0,0,0 ; bottom right to top, bottom right to left (outer)
	Line x+w,y+h,x+w,y
	Line x+w,y+h,x,y+h
	Color popuprectdr,popuprectdg,popuprectdb ; bottom right to top, bottom right to left (inner)
	y=y-1 : x=x-1
	Line x+w,y+h,x+w,y-1
	Line x+w,y+h,x+1,y+h
End Function
Function drawpopupline(num,x,y,newtext$,state) ; visuals
	If state = True Then
		Color 50,50,50
		Rect x,y+10,popupmaxwidth(num),FontHeight(),True
	End If
	Color 255,255,255
	;x = x + popupmaxwidth(num)/2
	
	Text x+10,y+10,newtext$,0,0
	
End Function
Function drawpopupln(x,y,w)
	y = y + 10 + (popuplineheight()/2)
	x=x+3
	w=w-9
	Color popuplinedr,popuplinedg,popuplinedb
	Line x,y,x+w,y
	Color popuplinelr,popuplinelg,popuplinelb
	y=y+1
	Line x,y,x+w,y
End Function
Function drawpopupsubitem(num,id,can) ; temp
	If popupdata(num,id,popuppointer) > -1 Then Return popupdata(num,id,popuppointer)
	Return -1
End Function
Function drawpopuparrow(x,y)
	; Needs buffering!!
	y=y+(popuplineheight())+3
	Color 0,0,0
	cnt=cnt+1
	y2# = 7
	Line x,y-4,x,y+4
	For x1=x To x+4
		Line x1,y,x1,y+(y2/2)
		Line x1,y,x1,y-(y2/2)
		y2#=y2#-1.7
	Next
End Function

Function popupmaxwidth(num)
	w=0
	For i=0 To numpopupitems
		If popupactive(num,i) = True Then
			If w < popupitemwidth(num,i) Then w = popupitemwidth(num,i)			
		End If
	Next
	w2 = popupminwidth(num)
	If w2 > w Then Return w2 + 20
	Return w + 20
End Function
Function popupmaxheight(num)
	Return (getpopupnumitems(num) * popupfontheight)+20
End Function
Function popupminwidth(num)
	If popupisinitiated(num) = False Then Return False 
	Return popupdata(num,0,popupminwidth)
End Function
Function popupitemwidth(num,id)
	If popupactive(num,id) = True Then		
		Return (Len(getpopuptext(num,id))) * popupfontwidth
	End If
End Function
Function popupitemheight(num,id)
	Return popupfontheight
End Function
Function popuplineheight()
	Return popupfontheight
End Function