Example of OOP GUI App needed.

BlitzMax Forums/BlitzMax Beginners Area/Example of OOP GUI App needed.

Amon(Posted 2006) [#1]
I've had to start from scratch with my app and want to go about it using OOP.

Can anyone post some code that shows how to create a window with a canvas but using OOP and being able to check for events?

It may sound like a lot but I kind of don't know where to start. In the mean time I'll start coding.

Thanks :)


CS_TBL(Posted 2006) [#2]
Strict

Type TAmon
	Field window:TGadget
	Field canvas:TGadget
	Field button:TGadget
	
	Field clsR:Byte
	Field clsG:Byte
	Field clsB:Byte
	
	Method ev()
		If EventSource()=canvas
			If EventID()=EVENT_GADGETPAINT
				update
			EndIf
			If EventID()=EVENT_MOUSEMOVE
				clsR=EventX()
				clsG=EventY()
				update
			EndIf
		EndIf
		If EventSource()=button
			clsR=Rnd(255)
			clsG=Rnd(255)
			clsB=Rnd(255)
			update
		EndIf
	End Method
	
	Method update()
		SetGraphics CanvasGraphics(canvas)
			SetClsColor clsR,clsG,clsB
			Cls
		Flip
	End Method
	
End Type


'
' a seperate create function to make it a bit more like the other blitz commands
'
Function CreateAmon:TAmon(x:Int,y:Int,w:Int,h:Int)

	Local a:TAmon=New TAmon
	a.window=CreateWindow("kamikaze",x,y,w,h)
	a.canvas=CreateCanvas(0,0,w-80,h,a.window)
	a.button=CreateButton("O_o",w-80,0,70,32,a.window)
	SetGadgetLayout a.canvas,1,0,1,0
	Return a
End Function



Local a:TAmon=CreateAmon(32,32,512,256)

OnEnd quit

Repeat
	WaitEvent()
	a.ev
	If EventID()=EVENT_WINDOWCLOSE End
Forever

Function quit()
	GCCollect()
	End
End Function


Note that it still has manual event-checking (a.ev), haven't 100% got yet how to do the eventhook stuff, and how to emit events. It's probably not that hard, but the manual is kinda minimalistic on that..


Amon(Posted 2006) [#3]
This is perfect. Thanks dude. :)


REDi(Posted 2006) [#4]
Heres a quick demo using an eventhook...




CS_TBL(Posted 2006) [#5]
Fab! Finally an eventhook example that isn't written for cyborgs! Examples should be minimalistic, lotsa examples are often large pieces o' non-modular code.

Papa^2 !

Now, can you add the same style o' tut to this about own events?

imagine this chunk being added to your mainloop, right under Waitevent()
	If EventID()= <New custom ID number here>
		If EventSource()=<ID of source here>
			Notify "papa!"
		EndIf
	EndIf


Now for example let a mousedown on that canvase create a new eventID. Meaning I can work with such an object *everywhere* (since EventID(), EventSource() and all the other Event-things are globals)
So in short: mousedown on canvas should trigger the notify 'papa!'


REDi(Posted 2006) [#6]
.
I try to keep the main loop empty, thats what events are for ;)


CS_TBL(Posted 2006) [#7]
no, globally I meant, not as part of the object, it really has to emit an event.

Imagine I'm making a GUI-object like a spinner orso, I simply create it like any other gadget, and anywhere in any part of the whole source, in any object, I should be able to read it out by checking the globals EventID() etc.


REDi(Posted 2006) [#8]


Mind you the custom event could also be caught by the eventhook :)


CS_TBL(Posted 2006) [#9]
Nicenice! For completeness and non-globalness I'd say:

Const CustomEvent = 1
(..)
Field NewEvent:TEvent = New TEvent
(..)
and
NewEvent.ID = CustomEvent
in Method New()

Saves globals.. the less globals the better, I always figure..!


CS_TBL(Posted 2006) [#10]
Ok, I think I get the hang of it,

just before the emitevent in that method I put this:
NewEvent.source=Self

Et Voila, in my mainloop I can now check on EventSource being App ...

and add this
NewEvent.x=Event.x
before the emitevent means I can readout EventX() in my mainloop ..

Ok, eventhook rocks, Papa next time you should write the bmax manual ^_^


REDi(Posted 2006) [#11]
You got it CS, its easier than it seems isn't it :)


CS_TBL(Posted 2006) [#12]
Well things being easy depends on who teaches it. As good as the manual for B+ was, as weak is the manual for Bmax. While I can understand that BRL doesn't always have the time to polish manuals, it's still highly required. B+'s manual was really excellent for that matter..
One could ask on the forum, but a *lot* of coders either have their backgrounds in game-programming, which seems to lead to a completely different code-style (probably NOT event-based), or aren't thinking modular enough to come up with the perfect example-code.


CS_TBL(Posted 2006) [#13]
I made some GUI-object, need to add some free-up method:

Method Free()
End Method

I figure I can kill the created gadgets easily with:

Method Free()
canvas=null
button=null
gccollect()
End Method

However, how do I get rid of the eventhook for this object? The manual is as zero'ish as it is ever:

Function RemoveHook( id,func:Object( id,data:Object,context:Object ) )

I figure 'id' could be 'self' ? And what is that func stuff/how to use it?


REDi(Posted 2006) [#14]
I've Updated the example with a Delete method, which is called automatically by BlitzMax when the object is released. (same as the new method is called when a new object is created)



CS_TBL(Posted 2006) [#15]
ah, nice.

When/how is an object released actually? When I do:
App=null
?

Or only after App=null and some garbagecollecting?

Or what else? :P


REDi(Posted 2006) [#16]
I think the GC calls the Delete method just before it releases the object from memory (when there is no more references to that object).


CS_TBL(Posted 2006) [#17]
I just tested it, I put a notify "bla" in the Delete method, but nothing is ever notified when I 'End' or 'App=null'..
My OnEnd function also runs a GCCollect, so plenty o' opportunities to show my those notifications.. sofar: zippo~


REDi(Posted 2006) [#18]
Yeah I've just been doing that too :(
Change Delete to Free and call app.free and it works.

Is it a bug BRL?

*EDIT* It works fine if you comment out the hook stuff. Seems that the GC never gets used because there is still a reference to the object... in the hook!

so its not really a bug, should be mentioned in the docs tho ;)


CS_TBL(Posted 2006) [#19]
well, bug or not, I figured a manual free-up would work. The primary question was about that RemoveHook anyway. I figure my first-ever-hooked-gadget is kinda ready then, a multimulti-functional spinnergadget. ^_^
One of these days it'll appear in the code-archives. Sofar it works like other gadgets (except the gadget-modifying like Setgadgetshape etc. but I rarely/never use that anyway).

An interesting thread this became, it by now contains a full howto for eventhooks, far better understandable than any other attempts I've seen sofar!


CS_TBL(Posted 2006) [#20]
papa to the rescue!

Move the green windows around and observe! ._.

SuperStrict
Type Tbla
	
	Field canvas:TGadget
	Field c:Int

	Function eventhook:Object(id:Int,data:Object,context:Object)
		If Tbla(context) Tbla(context).ev TEvent(data);Return data	
	EndFunction
	
	Method New()
		AddHook EmitEventHook,eventhook,Self
	End Method
	
	Method Free()
		RemoveHook EmitEventHook,eventhook
		GCCollect()
	End Method
	
	Method ev(event:TEvent)
		If Event.source=canvas
			If Event.id=EVENT_GADGETPAINT update
		EndIf
	End Method
	
	Method update()
		SetGraphics CanvasGraphics(canvas)
			SetClsColor c,255-c,c;Cls
		Flip
	End Method
			
End Type

Function CreateBla:Tbla(parent:TGadget)
	Local a:TBla=New TBla
	a.canvas=CreateCanvas(0,0,128,128,parent)
	a.c=Rnd(255)
	Return a
End Function

Local window:TGadget=CreateWindow("o_O",0,0,640,480)
Local w1:TGadget=CreateWindow("1",50,50,128,128,window,1|WINDOW_CHILD)
Local w2:TGadget=CreateWindow("2",90,90,128,128,window,1|WINDOW_CHILD)

Local bla1:TBla=CreateBla(w1)
Local bla2:TBla=CreateBla(w2)

Repeat
	WaitEvent()
	If EventID()=EVENT_WINDOWCLOSE End
Forever


The WINDOW_CHILD is basically to blame here, without it there's no harm. However I want to make sure it's something I'm doing wrong/could fix, before it goes to the bug-submit orso.


REDi(Posted 2006) [#21]
Its a known issue...
http://www.blitzmax.com/Community/posts.php?topic=55777


Gavin Beard(Posted 2006) [#22]
hi with the following code:
Strict

Type TTestApp

	Field Window:TGadget = CreateWindow("",0,0,640,480)
	Field Button:TGadget = CreateButton("BUTTON",0,0,100,20,Window)
	Field Canvas:TGadget = CreateCanvas(0,20,ClientWidth(window),ClientHeight(window)-20,window,PANEL_BORDER)
	
	Method New()
		AddHook EmitEventHook,eventhook,Self
		Button.SetLayout(1,0,1,0)
		Canvas.SetLayout(1,1,1,1)
	EndMethod	
	
	Function eventhook:Object(id,data:Object,context:Object)
		If TTestApp(context) Then TTestApp(context).OnEvent TEvent(data)
		Return data	
	EndFunction
	
	Method OnEvent(event:TEvent)
		Select event.id
		
			Case EVENT_WINDOWCLOSE
				Select Event.Source
					Case window
						End
				End Select
				
			Case EVENT_GADGETACTION
				Select Event.Source
					Case Button
						Notify "You pressed the button"
				EndSelect
			
			Case EVENT_GADGETPAINT
				Select Event.Source
					Case Canvas
						SetGraphics(CanvasGraphics(canvas))
						SetViewport(0,0,ClientWidth(canvas),ClientHeight(canvas))
						Cls
						DrawText "TEST CANVAS",10,10
						Flip 0
				EndSelect
				
		EndSelect
	EndMethod
	
EndType


Global App:TTestApp = New TTestApp

While True
	WaitEvent()
Wend


should i not be able to do

Global App:TTestApp = New TTestApp
Global App2:TTestApp = New TTestApp


...although if i do it throws and error with the line?


tonyg(Posted 2006) [#23]
Works for me.
What error do you get?


Gavin Beard(Posted 2006) [#24]
Global App:TTestApp = New TTestApp by itself is ok,

but if i do
Global App:TTestApp = New TTestApp
Global App2:TestApp = New TTestApp

it stops at the event.id with an error about accessing property of a null object?


tonyg(Posted 2006) [#25]
You said...
global app2:TestApp = New TTestApp
rather than
global app2:TTestApp = New TTestApp
Correcting that on Bmax 1.16 and it works as expected.


Gavin Beard(Posted 2006) [#26]
sorry just my spelling mistake, it is TTestApp - its just my keyboard at work and the keys stick...lol

will try again at home