Example of OOP GUI App needed.

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

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 :)

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..

This is perfect. Thanks dude. :)

Heres a quick demo using an eventhook...

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


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!'

.
I try to keep the main loop empty, thats what events are for ;)

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.

Strict

Global CustomEvent = 1

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
				
			Case EVENT_MOUSEDOWN
				Select Event.Source
					Case Canvas
						Local NewEvent:TEvent = New TEvent
						NewEvent.ID	= CustomEvent
						EmitEvent(NewEvent)
				EndSelect
			
		EndSelect
	EndMethod
	
EndType


Global App:TTestApp = New TTestApp

While True
	WaitEvent()
	If EventID() = CustomEvent Then Notify "PAPA!"
Wend


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

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..!

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 ^_^

You got it CS, its easier than it seems isn't it :)

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.

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?

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)
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	
	
	Method Delete()
		RemoveHook(EmitEventHook,eventhook)
		FreeGadget(Window)
		FreeGadget(Button)
		FreeGadget(Canvas)
	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


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

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).

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~

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 ;)

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!

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.

Its a known issue...
http://www.blitzmax.com/Community/posts.php?topic=55777

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?

Works for me.
What error do you get?

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?

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.

sorry just my spelling mistake, it is TTestApp - its just my keyboard at work and the keys stick...lol

will try again at home