glade -> maxgui -> event callbacks

BlitzMax Forums/BlitzMax GUI Programming/glade -> maxgui -> event callbacks

Heres the prize!
example.bmx
Import "loadglade.bmx"

LoadGlade("test1\test1.glade")

Global edit1:guiwrap=findGadget("entry1")
Global label1:guiwrap=findGadget("label1")
Global button1:guiwrap=findGadget("button1")

Global mainwindow:guiwrap=findGadget("window1")

' you can share callback functions ie button2.setActionCallBack(quitme)
mainwindow.setCloseCallBack(quitme)
edit1.setActionCallBack(edit1action)
button1.setActionCallBack(button1action)

processGUI()

Function quitme()		;	End		;	End Function

Function edit1action()	;	SetGadgetText(label1.maxgad,GadgetText(edit1.maxgad))	;	End Function

Function button1action()
	SetGadgetText(edit1.maxgad,"")
	SetGadgetText(label1.maxgad,"")
End Function

Dunno about you but I'm tired of long winded event select clauses... Delphi introduced me to event callback ages ago and its a powerful idea.

For the time being I'm using a couple of Tmap's to convert pointers but I could get round the need for both if only all BRL objects had a UserData:Object field...

andhow its a bit hackerish cause I tried out a bunch of different ideas.

Idealy it would need a more specific editor (but glade will fill the gap for a fair while!) it only has callbacks for 2 event types three gui object types and the minimum of gadget properties.

That said even with such a limited implementation it shows the convienience of this approch in terms of rapid application development.

I'd really welcome peoples feedback on this as I think that a community user interface system like this would greatly speed up gui development


loadglade.bmx
Import BaH.Libxml

Global gadList:TList=New TList
Global pointermap:tmap=New Tmap
Global gadgetmap:tmap=New Tmap

Type guiWrap 
	Field maxgad:Tgadget
	Field props:tmap
	
	Field closeCallback()=Null
	Field actionCallback()=Null
	
	Function create:guiWrap()
		Local g:guiWrap=New guiWrap
		g.props=New Tmap
		Return g
	EndFunction
	
	Method setCloseCallBack(ccb:Byte Ptr)
		closeCallback=ccb
	EndMethod

	Method setActionCallBack(acb:Byte Ptr)
		actionCallback=acb
	EndMethod
EndType

Function processGUI()
	While WaitEvent()
		Local eg:Tgadget
		Local gw:guiwrap
		eg=Tgadget(EventSource())
		If eg Then gw=guiwrap(MapValueForKey(gadgetmap,eg))
		Select EventID()
			Case EVENT_WINDOWCLOSE
				If gw.closeCallback Then gw.closeCallback()
			Case EVENT_GADGETACTION
				If gw.actionCallback Then gw.actionCallback()
		End Select
	Wend
EndFunction


Function findGadget:guiwrap(name:String)
	Return guiwrap(MapValueForKey(pointermap,name))
EndFunction

Function loadGlade(docname:String)

	parseDoc(docname)
	createInteface()

EndFunction

Function createInteface()
	Global lastwindow:guiwrap

	For Local g:guiWrap=EachIn GadList
		Local id:String,x:Int,y:Int,w:Int,h:Int,title:String,label:String,text:String
		Local parentid:guiWrap,pid:String

		id=String(g.props.ValueForKey("id"))
		x=Int(String(g.props.ValueForKey("x")))
		y=Int(String(g.props.ValueForKey("y")))
		w=Int(String(g.props.ValueForKey("width_request")))
		h=Int(String(g.props.ValueForKey("height_request")))
		title=String(g.props.ValueForKey("id"))
		label=String(g.props.ValueForKey("label"))
		text=String(g.props.ValueForKey("text"))
		pid=String(g.props.ValueForKey("id"))

		If w=0 Or h=0 Then Print "WARNING you must explicitly set width and height in glade for "+pid

		Select String(g.props.ValueForKey("class"))
			Case "GtkWindow"
				g=guiWrap.create()
				g.maxgad=CreateWindow(title,x,y,w,h)
				lastwindow=g	' this is a temp bodge  todo can gadget parents be specified after creation?
			Case "GtkButton"
				g=guiWrap.create()
				g.maxgad=CreateButton(label,x,y,w,h,lastwindow.maxgad)
			Case "GtkLabel"
				g=guiWrap.create()
				g.maxgad=CreateLabel(label,x,y,w,h,lastwindow.maxgad)
			Case "GtkEntry"
				g=guiWrap.create()
				g.maxgad=CreateTextField(x,y,w,h,lastwindow.maxgad)
				SetGadgetText g.maxgad,text
		EndSelect
		pointermap.insert(pid,g)
		If g.maxgad Then gadgetmap.insert(g.maxgad,g)
	Next

EndFunction

' todo check if gtkfixed causes "null" object....
Function parseNodeChildren(doc:TxmlDoc, node:TxmlNode, parentid:String,skip:Int=False,ntype:String="")
	Global currentGad:guiWrap

	Local uri:String
	
	Local list:TList = node.getChildren()

	For node = EachIn list

		If node.getName() = "widget" Then
			currentGad=guiWrap.create()
			ListAddLast GadList,currentGad
			If node.getattribute("class")<>"GtkFixed" Then
				currentGad.props.insert("class",node.getattribute("class"))
				MapInsert(currentGad.props,"id",node.getattribute("id"))
				MapInsert(currentGad.props,"parentid",parentid)
				parentid=node.getattribute("id")
				parseNodeChildren(doc,node,node.getattribute("id"))
			Else 
				parseNodeChildren(doc,node,parentid,True)
			End If
		End If
		
		If node.getName() = "child" Then parseNodeChildren(doc,node,parentid)
		If node.getName()= "property" And Not skip Then	MapInsert(currentGad.props,node.getattribute("name"),node.getContent())
		If node.getName()= "packing" And Not skip Then parseNodeChildren(doc,node,parentid,False,"packing")
	Next

End Function

Function parseDoc(docname:String)

        Local doc:TxmlDoc
        Local node:TxmlNode
        doc = TxmlDoc.parseFile(docname)
        
        If doc = Null Then
                Print "Document not parsed successfully."
                Return
        End If
        
        node = doc.getRootElement()
        If node = Null Then
                Print "empty document"
                doc.free()
                Return
        End If
        
        If node.getName() <> "glade-interface" Then
                Print "document of the wrong type, root node <> glade-interface"
                doc.free()
                Return
        End If
        
        parseNodeChildren(doc, node, "ROOT")
        doc.free()
End Function


test1.glade (load and visually edit widgets with glade)
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">

<glade-interface>

<widget class="GtkWindow" id="window1">
  <property name="width_request">500</property>
  <property name="height_request">400</property>
  <property name="visible">True</property>
  <property name="title" translatable="yes">window1</property>
  <property name="type">GTK_WINDOW_TOPLEVEL</property>
  <property name="window_position">GTK_WIN_POS_NONE</property>
  <property name="modal">False</property>
  <property name="resizable">True</property>
  <property name="destroy_with_parent">False</property>
  <property name="decorated">True</property>
  <property name="skip_taskbar_hint">False</property>
  <property name="skip_pager_hint">False</property>
  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
  <property name="focus_on_map">True</property>
  <property name="urgency_hint">False</property>

  <child>
    <widget class="GtkFixed" id="fixed1">
      <property name="visible">True</property>

      <child>
	<widget class="GtkButton" id="button1">
	  <property name="width_request">52</property>
	  <property name="height_request">25</property>
	  <property name="visible">True</property>
	  <property name="can_focus">True</property>
	  <property name="label" translatable="yes">button1</property>
	  <property name="use_underline">True</property>
	  <property name="relief">GTK_RELIEF_NORMAL</property>
	  <property name="focus_on_click">True</property>
	</widget>
	<packing>
	  <property name="x">40</property>
	  <property name="y">72</property>
	</packing>
      </child>

      <child>
	<widget class="GtkEntry" id="entry1">
	  <property name="width_request">158</property>
	  <property name="height_request">23</property>
	  <property name="visible">True</property>
	  <property name="can_focus">True</property>
	  <property name="editable">True</property>
	  <property name="visibility">True</property>
	  <property name="max_length">0</property>
	  <property name="text" translatable="yes"></property>
	  <property name="has_frame">True</property>
	  <property name="invisible_char">*</property>
	  <property name="activates_default">False</property>
	</widget>
	<packing>
	  <property name="x">96</property>
	  <property name="y">144</property>
	</packing>
      </child>

      <child>
	<widget class="GtkLabel" id="label1">
	  <property name="width_request">160</property>
	  <property name="height_request">16</property>
	  <property name="visible">True</property>
	  <property name="label" translatable="yes">label1</property>
	  <property name="use_underline">False</property>
	  <property name="use_markup">False</property>
	  <property name="justify">GTK_JUSTIFY_LEFT</property>
	  <property name="wrap">False</property>
	  <property name="selectable">False</property>
	  <property name="xalign">0.5</property>
	  <property name="yalign">0.5</property>
	  <property name="xpad">0</property>
	  <property name="ypad">0</property>
	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
	  <property name="width_chars">-1</property>
	  <property name="single_line_mode">False</property>
	  <property name="angle">0</property>
	</widget>
	<packing>
	  <property name="x">96</property>
	  <property name="y">176</property>
	</packing>
      </child>
    </widget>
  </child>
</widget>

</glade-interface>


if the callback functions were in gui editor properties then the whole code could be automatically created...