your own gadgets! (Blitzplus, average skills)

BlitzPlus Forums/BlitzPlus Tutorials/your own gadgets! (Blitzplus, average skills)

CS_TBL(Posted 2004) [#1]
When the B+ gadgets don't satisfy your needs anymore, and you fear that BlitzMax will still take a while, then read on!

Using banks, you can do your own gadgets, which can be way more advanced than any 'internal' gadget. The basic idea is that you store all your gadget-properties in a bank, and use the bankhandle for everything gadget-related (drawing, modifying etc.)

Compare:

window=CreateWindow("title",0,0,640,480)

with

bla=CreateSomething(32,32,64,24,window)

From this perspective, working with banks is the most clean method to do your own gadgets. And after adding "CreateSomething(x,y,width,height,parent)" to a .decl file to make it highlight in your code, it feels like you're working with an internal gadget!

The only 2 downsides are that you need your own events-checks and that the normal gadget modifers (SetGadgetShape etc. etc.) won't work on these new gadgets). But personally I'd say these downsides are really no match for all the possible goodies that are in store for you!

Basically there're several functions that you always need:
- a create function
- an update function

and if the function should react to events then you need another one:
- an event function

additionally, if you wish to read or write properties of your gadget then you need these 2 functions aswell:
- get functions
- set functions

A create function creates a bank for you in which to store all your properties.

An update function draws your gadget.

An event function does all the event handling and can modify your gadget properties.

And set & get functions write or read properties.

Note that once you created your own gadget, the gadget handle is the only variable you'll ever need in your source. You don't need globals or other vars, for the simple reason that all vars are stored inside the gadget-bank.

Another example of custom gadget I already made is a window. This window has a border based on a bitmap and with a get function you get back the panel of that custom window. On this panel you can place blitz gadgets or your own gadgets. With a little effort you can make a gadget that contains even more gadgets. In this case the sub-gadget event-checks are inside the parent event-checks. In short, a scrollbar would be made out of 1 main gadget with 3 subgadgets (an up-button, a down-button and the scrollbar itself). This works perfectly! And you need only one event function call in your mainloop.

With a little more creativity you can load images inside a create-function, and store the image-handles inside the bank. A primitive image-button is within everyone's reach then.

There are more ways to enhance the whole gadget system. You could for example use the first 4 bytes of each bank to store some ID. By checking on the ID you can make sure that you don't accidentaly send the wrong bankhandle to an eventchecker, updater or get/set.

Gadgets can be complete large objects, one of my own gadgets is a complete zoom window (to draw tiles/sprites). Once I created it, and once I put an events checker in my mainloop I can draw in it with 2 colors! This way, building apps is a piece o' cake!

The source below is as small as possible, without all the comfy extras, to make it a bit more readable. The color-system could be somewhat more advanced as well (didn't want to spend more than a few seconds to it :), but the whole idea is clear.

;
; your own gadgets in Blitzplus using banks
;
; tutorial by CS^TBL - 2004
;


app=CreateWindow("gadget example",0,0,640,480)

; a single something
something=CreateSomething(32,32,64,24,app,80,120,160)


; an array of somethings  (nice way to do a virtual drumcomputer this way ^^;)

Dim bla(31)

For t=0 To 31

	bla(t)=CreateSomething(32+(t*16),128,16,32,app,192,0,0) ; create a row of somethings
	
	If Rnd(1,5)<2 SetSomethingState(bla(t),True) ; switch on a few
	
Next


;----------------------------------------------------------------------------------------------------
; ----- MAIN loop -----------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------
quit=False

Repeat
	WaitEvent()
	If EventID()=$803 quit=True


	; check on events
	SomethingEvents(something)
	
	For t=0 To 31
		SomethingEvents(bla(t))
	Next
	
Until quit

;----------------------------------------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------

; and free all the somethings again
FreeBank something
For t=0 To 31
	FreeBank bla(t)
Next

End ; bye!


;----------------------------------------------------------------------------------------------------
Function GetSomethingState(bank)
	If BankSize(bank)<8 End
	Return PeekByte(bank,4)
End Function

;----------------------------------------------------------------------------------------------------
Function SetSomethingState(bank,state)
	If BankSize(bank)<8 End
	PokeByte bank,4,state 
	UpdateSomething(bank)
End Function

;----------------------------------------------------------------------------------------------------
Function SomethingEvents(bank)

	If BankSize(bank)<8 End

	canvas=PeekInt(bank,0)
	status=PeekByte(bank,4)
	
	If EventSource()=canvas
	
		If EventID()=$201
		
			If EventData()=1

				status=(status+1) Mod 2
				
				PokeByte bank,4,status
				
				UpdateSomething(bank)

			EndIf
		EndIf
	
	EndIf	

End Function

;----------------------------------------------------------------------------------------------------
Function UpdateSomething(bank)

	If BankSize(bank)<8 End

	canvas=PeekInt(bank,0)
	status=PeekByte(bank,4)

	r=PeekByte(bank,5)
	g=PeekByte(bank,6)
	b=PeekByte(bank,7)
	
	width=GadgetWidth(canvas)
	height=GadgetHeight(canvas)
	
	SetBuffer CanvasBuffer(canvas)
	
		Color 0,0,0:Rect 0,0,width,height,False
		Color 255,255,255:Rect 1,1,width-2,height-2,False
		
		r=Limit(r+(status*64),0,255)
		g=Limit(g+(status*64),0,255)
		b=Limit(b+(status*64),0,255)

		Color r,g,b

		Rect 2,2,width-4,height-4,True
		
	FlipCanvas canvas

End Function

;----------------------------------------------------------------------------------------------------
Function CreateSomething(x,y,width,height,parent,r,g,b)

	; adr	name			size
	; --------------------------------------
	; 00	canvas		1 int ( 4 bytes)
	; 04	status		1 byte
	; 05	red			1 byte
	; 06	greens		1 byte
	; 07	blue			1 byte
	;
	; total: 8 bytes
	
	bank=CreateBank(8)
	canvas=CreateCanvas(x,y,width,height,parent)
	
	PokeInt bank,0,canvas
	PokeByte bank,4,status
	PokeByte bank,5,r
	PokeByte bank,6,g
	PokeByte bank,7,b
	
	UpdateSomething(bank)
	
	Return bank
	
End Function

;----------------------------------------------------------------------------------------------------
;-- just a handy global function --------------------------------------------------------------------
;----------------------------------------------------------------------------------------------------
Function Limit(value,minvalue,maxvalue)
	If value<minvalue Return minvalue
	If value>maxvalue Return maxvalue
	Return value
End Function



Red(Posted 2004) [#2]
Very interesting


Agamer(Posted 2004) [#3]
LOoks a good way to make up for the lack of gadjets


CS_TBL(Posted 2004) [#4]
here's another example.. a scroll-panel

Notice that found a dumb thingy while preparing this post.. I'm too lazy to fix it however.. :)

For obvious reasons the scrollpanel needs to be as wide as the parent-panel.. so having an arguement stating the width of you scrollpanel would'n much useful when you can get the gadgetwidth of the parent panel to get that width ;)


anyway (it's just an example, soon enough you should be making your own gadgets anyway), enjoy..

app=CreateWindow("",0,0,640,480)


; create 2 parent panels
panel1=CreatePanel(20,20,256,256,app)
panel2=CreatePanel(320,20,256,256,app)

; create the scrollpanels
scrollpanel1=CreateVScrollpanel(256,768,panel1,32,160,160,160)
scrollpanel2=CreateVScrollpanel(256,512,panel2,32,160,160,160)

; put some example crap on the scrollpanels
For t=0 To 10
	CreateButton(t,32,20+t*64,64,24,VScrollpanelPanel(scrollpanel1)) 
	CreateButton(t,32+t*5,20+t*32,64,24,VScrollpanelPanel(scrollpanel2)) 
Next

; main loop
quit=False
Repeat
	WaitEvent()
	
	If EventID()=$803 quit=True

	; if you don't include these, you can't scroll!	
	VScrollpanelEvents (scrollpanel1)
	VScrollpanelEvents (scrollpanel2)
	
Until quit

; clean-up
FreeBank scrollpanel1
FreeBank scrollpanel2

; bye!
End







; returns the actual panelhandle of the scrollpanel..
Function VScrollpanelPanel(bank)
	Return PeekInt(bank,4)
End Function



Function VScrollpanelEvents(bank)

	canvas=PeekInt(bank,8)
	
	If EventSource()=canvas
	
		If EventID()=$201 
			PokeByte bank,19,1 ; drag=true
			PokeShort bank,20,MouseY() ; start coord.
		EndIf
		
		If EventID()=$202
			PokeByte bank,19,0 ; drag=false
		EndIf
		
		If PeekByte(bank,19) ; drag ?
			If EventID()=$203
			
				y=MouseY()
				
				panel=PeekInt(bank,4)
				oldy=PeekShort(bank,20)
				
				ycoord=GadgetY(panel)+y-oldy
				ycoord=Limit(ycoord,-PeekShort(bank,14)+PeekShort(bank,22),0)
				
				SetGadgetShape panel,GadgetX(panel),ycoord,GadgetWidth(panel),GadgetHeight(panel)

				PokeShort bank,20,y
				
			EndIf
		EndIf
		
	EndIf

End Function

Function CreateVScrollpanel(width,height,parent,dragwidth,r,g,b)

	If width*height=0 Break"Incorrect dimensions!"
	If height<GadgetHeight(parent) Break"Panel height-bigger than parent-height!"
	If Not parent Break"Parent missing"
	If Not dragwidth Break"Dragbar zero"
	r=Limit(r,0,255)
	g=Limit(g,0,255)
	b=Limit(b,0,255)
	
	; adr	name		size
	; 00	ID			4 bytes    <- reserved for an optional 4-chars ID check! not used right now..
	; 04	panel		1 int (4 bytes)
	; 08	dragcanvas	1 Int (4 bytes)
	; 12	width		1 short (2 bytes)
	; 14	height		1 short (2 bytes)
	; 16	r			1 byte
	; 17	g			1 byte
	; 18	b			1 byte
	; 19	dragstatus	1 byte
	; 20	dragY		1 short (2 bytes)
	; 22	parntheight	1 short (2 bytes)

	bank=CreateBank(24)
	
	panel=CreatePanel(0,0,width,height,parent)
	SetPanelColor panel,r,g,b
	canvas=CreateCanvas(width-dragwidth,0,dragwidth,height,panel)
	SetCanvas canvas
		ClsColor r,g,b:Cls
		For y=2 To height-1 Step 4
			For x=2 To dragwidth-1 Step 4
				xo=(y Mod 8)/4
				Bcolor r*1.3,g*1.3,b*1.3
				Plot x+xo,y
				Bcolor r*0.7,g*0.7,b*0.7
				Plot x+xo+1,y+1
			Next
		Next
		Bcolor r*1.3,g*1.3,b*1.3
		Line 0,0,0,height-2
		Line 0,0,dragwidth-2,0
		
		Bcolor r*0.7,g*0.7,b*0.7
		Line dragwidth-1,1,dragwidth-1,height-2
		Line 1,height-1,dragwidth-2,height-1
		
	FlipCanvas canvas
	
	PokeInt bank,4,panel
	PokeInt bank,8,canvas
	PokeShort bank,12,width
	PokeShort bank,14,height
	PokeByte bank,16,r
	PokeByte bank,17,g
	PokeByte bank,18,b
	
	PokeShort bank,22,GadgetHeight (parent)
	
	Return bank

End Function

; misc. needed global functions.. some useful, some just for the lazy coder (SetCanvas) :)

Function Limit(value,minvalue,maxvalue)
	If value<minvalue Return minvalue
	If value>maxvalue Return maxvalue
	Return value
End Function

Function Break(s$)
	Notify s$
	End
End Function

Function Bcolor(r,g,b)
	r=Limit(r,0,255)
	g=Limit(g,0,255)
	b=Limit(b,0,255)
	Color r,g,b
End Function

Function SetCanvas(bla)
	SetBuffer CanvasBuffer(bla)
End Function



Stamm(Posted 2010) [#5]
That's really very useful.