GuiAnsatz: Button funktion

Übersicht BlitzMax, BlitzMax NG Allgemein

Neue Antwort erstellen

 

PhillipK

Betreff: GuiAnsatz: Button funktion

BeitragDi, Dez 25, 2012 21:20
Antworten mit Zitat
Benutzer-Profile anzeigen
Huhu!
Ich grübel grade, was der beste weg ist, einem user potentielle click-informationen zukommen zu lassen, welches buttons (und generell objekte in meiner gui) betrifft.

Da ich das eventbasierte system von MaxGui einen graus finde, möchte ich hier etwas anderes schaffen (und weil ich keine ahnung habe, wie ich ein TEvent auslöse.. wäre aber vielleicht auch cool zu wissen Very Happy)

Mir schweben da mehrere ideen vor:

wxWidgets abart:
- Die grundklassen stelle ich zur verfügung und biete jedem nutzer die möglichkeit, diverse methoden selbst zu definieren.
- Die Grundfunktionen "OnClick" "OnMouseEnter" usw werden zwar beigeliefert, sind aber leer.

- Vorteil:
- Völlige kontrolle über alles.
- Schön sauber abgepackt, alles was man braucht
- Nachteil:
- Übersicht! Wird extrem überladen, wenn man beispielsweise 100 button-types hat
- Casten! Wenn man explizit eines seiner button-abarten rausfiltern möchte, könnte es ewig große Cast-bäume geben *wurgs*


Die 2te idee ist eine art RegisterFunction. Dies soll eine Methode in der CoreKlasse eines jeden TguiElements sein, welches funktionen + konstante verwaltet. Beispiel:
Button.RegisterFunction( MOUSE_ENTER, myFunc(obj:TguiElement))

- Vorteil:
- Ebenso große kontrolle über alles was möglich ist (Mouse Enter, Mouse Leave, Mouse Click, Mouse Rightclick, ...) abgepackt und eigens zu schreibende funktionen
- Mehrfachverwendung des selben button types
- Nachteil:
- Übersicht Sad Ebenso, massiver spam an hunderten von funktioenn, wenn man vieles bastelt

Die dritte idee nähert sich wieder dem Eventsystem. Geplant ist hier ein stack, der ebenso Konstante wie GuiObject verbindet und pro frame geleert wird. Zusätzlich könnte ich mir diverse funktionen im entsprechenden Gui core vorstellen, ala GetNextClicked:TGuiElement(), welches den stack nach allen MouseHit(1) events durchsucht und das entsprechende objekt zurückgibt.

- Vorteil:
- Man kann die bäume einfacher wie beim normalen Eventsystem abarbeiten (kein riesen select / case)
- Der nutzer kann ganz speziell und objektbezogen agieren, ebenso als würde er die funktionen alle definieren. Oder aber der definiert die funktionen selber

- Nachteil:
- Warum das rad neu erfinden.. das eventsystem tuts dann auch.


Ich bin mir einfach unsicher, welcher weg der beste wäre.
Da über diese idee alle Interaktionen gehandelt werden sollen, sollte es intuitiv zu nutzen sein.
Ein weiterer dorn im auge ist mir die geplante lade / speicherfunktion einer programmierten gui. Das macht meine lieblingsidee (mit funktionspointern zu arbeiten) ziemlich unattraktiv.

(Laden, speichern: Alle elemente eines GuiCores sollen in einem XML baum oder ähnlichen an ihrer aktuellen position mit aktuellen werten gespeichert werden. So können häufig genutzte layouts abgespeichert werden und man muss sie nicht für jedes Projekt einzeln neu "einprogrammieren" bzw rumkopieren. Simples laden reicht aus, danach die buttonfunktionen etc belegen und gut ist Smile )

ZEVS

BeitragDi, Dez 25, 2012 21:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Zur ersten Idee:
Was hälst du von mehrfacher Ableitung? Also TGuiElement -> TButton -> TMeineButtonAbarten -> TMeineButtonAbart53. In TButton kannst du allgemeine, nützliche Funktionen hierzu definieren (z.B. eine default-Funktion, die einfach den Button heller werden lässt). In TMeineButtonAbarten definiere ich diejenigen Funktionen, die mir die Arbeit in den TMeineButtonAbart1-100 einfacher machen. Das Cast-Problem löst sich auch so.
Zur zweiten Idee:
Statt Funktionspointern kannst du auch Objekte übergeben (ich habe hier irgendwo gelesen, dass man das auch als Strategy-Pattern bezeichnet). D.h:
BlitzMax: [AUSKLAPPEN]
Type TGuiCallback Abstract
Method Call(obj:TGuiElement) Abstract
End Type

Wenn ich dann mehrere verschiedene Abarten hiervon definiere, kann ich das durch Abstraktion elegant lösen.
Zur dritten Idee:
Ich persönlich finde die Dokumentation zu BRL.Event völlig ausreichend, um zu verstehen, worum es geht.
Zum Laden/Speichern:
Wenn du statt Funktionspointern Objekte verwendest, kannst du den Benutzer auch zwingen, Methoden zu definieren, über die man "Funktionspointer" in XML speichern und laden kann. Außerdem kannst du darüber nachdenken, einen Parser zu schreiben, der XML-Bäume in BMax-Code umwandelt, sodass man die Datei mittels Include einbinden kann (für Releases ganz praktisch, wenn man die Ladezeit verkürzen möchte und will, dass der Anwender seine Finger aus Dingen lässt, in denen er nichts zu suchen hat).

ZEVS
 

PhillipK

BeitragDi, Dez 25, 2012 22:40
Antworten mit Zitat
Benutzer-Profile anzeigen
oh oh.. mein hirn hat sich grade ausgeschaltet.

Der part mit dem XML baum (die man per include einbauen kann.. ? ) habe ich so garnicht verstanden. In meinem horizont muss jede Bmx datei compilebar sein. Kannst du mir ein "minimales beispiel" hierzu geben?

Wobei beim 2ten lesen..
Gui Speichern: Die geladenen daten etc in einen xml baum oder ähnliches speichern.
Beim laden dann ein kleines programm, was diesen "xml baum oder ähnliches" wieder in eine bmx datei umwandelt, welche per include eingebunden und darüber die Gui wieder in ein TGuiCore umgewandelt werden kann.

Zur TguiCallback sache:
Wenn ich das richtig verstehe, ist hier die idee, ein grund-type mitzuliefern. Pro gewünschte art (MOUSE_HIT oder sonstwas) wird eines dieser TGuiCallbacks ausgelöst, die der user definiert hat...
Hmhm, nur wie ich den code hier variable speichern kann ist mir noch unklar.
In der parser idee würde hier der includete code entweder - würde ich reflection kennen / können - per fehlermeldung nach den types fragen.

Hui, schwerer als ich gedacht habe :3

ZEVS

BeitragDi, Dez 25, 2012 23:27
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich habe mal schnell etwas programmiert, dass die Reflection für dich übernehmen sollte.
BlitzMax: [AUSKLAPPEN]
SuperStrict

Type TExampleType

Field integer:Int
Field str$
Field samp:TExampleType

Method __sleep$[]() 'Vgl. http://php.net/manual/en/language.oop5.magic.php#object.wakeup

DebugLog "sleep"
Return ["integer", "samp"]

End Method

Method __wakeup()

DebugLog "wakeup"

End Method


End Type

Local samp:TExampleType = New TExampleType
samp.integer = 123
samp.str = "Hello World & Co."

Local innerSamp:TExampleType = New TExampleType
samp.samp = innerSamp
innerSamp.integer = 42
innerSamp.str = "<|>"

Local xml$ = ObjectToXML(samp)
Print xml
Local cp:TExampleType = TExampleType(XMLToObject(xml))
DebugStop


Function XMLEntities$(str$)
str = Replace(str, "&" , "&" )
str = Replace(str, "<" , "<" )
str = Replace(str, ">" , ">" )
str = Replace(str, "~q", """)
Return str
End Function

Function XMLRemoveEntities$(str$)
str = Replace(str, "<" , "<" )
str = Replace(str, ">" , ">" )
str = Replace(str, """, "~q")
str = Replace(str, "&" , "&" )
Return str
End Function


Function ConvertObject$(o:Object)

If Not o Then Return "Null"

Local tid:TTypeId = TTypeId.ForObject(o)
Local name$ = tid.Name()

Local str$

Local fields:TList

Local smth:TMethod = tid.FindMethod("__sleep")
If smth Then

Local result$[] = String[] (smth.Invoke(o, New Object[0]))
fields = New TList
For Local fname$ = EachIn result

Local fld:TField = tid.FindField(fname)
If Not fld Then RuntimeError "__sleep returned field "+fname+", which could not be found in "+name
fields.AddLast fld

Next

Else

fields = tid.EnumFields()

EndIf

For Local fld:TField = EachIn fields

Local fname$ = fld.Name()
str :+ "<"+fname+">"

Select fld.TypeId()

Case ByteTypeId, ShortTypeId, IntTypeId

str :+ fld.GetInt(o)

Case LongTypeId

str :+ fld.GetLong(o)

Case FloatTypeId

str :+ fld.GetFloat(o)

Case DoubleTypeId

str :+ fld.GetDouble(o)

Case StringTypeId

str :+ XMLEntities(fld.GetString(o))

Default

str :+ ConvertObject(fld.Get(o))

End Select
str :+ "</"+fname+">"

Next

Return str

End Function

Function ObjectToXML$(o:Object)

Local tid:TTypeId = TTypeId.ForObject(o)
Local name$ = tid.Name()

Return "<"+name+">"+ConvertObject(o)+"</"+name+">"

End Function

Function ConvertXML:Object(markup$, tid:TTypeId)

DebugLog "ConvertXML: "+markup+" ["+tid.Name()+"]"

If markup.Trim().ToLower() = "null" Then Return Null

Local o:Object = tid.NewObject()
Repeat

markup = markup.Trim()
If markup = "" Then Exit
If markup[0] <> Asc("<") Then RuntimeError "Error in XML Setup"
Local pos:Int = markup.Find(">")
If pos = -1 Then RuntimeError "Error in XML Setup"
Local name$ = markup[1..pos]
If Not name Then RuntimeError "Error in XML Setup"
Local fld:TField = tid.FindField(name)
If Not fld Then RuntimeError "Referring to unknown field "+name+" of type "+tid.Name()
Local open:Int = 1
Local start:Int = pos+1
pos = 0
Repeat

pos = markup.Find("<", pos+1)
If pos = -1 Or pos = markup.length-1 Then RuntimeError "Error in XML Setup"
If markup[pos+1] = Asc("/") Then

open :- 1

Else

open :+ 1

EndIf

Until open = 0
Local slice$ = markup[pos+2..pos+2+name.length]
If slice <> name Then RuntimeError "Error in XML Setup"
Local innerMarkup$ = markup[start..pos]
Select fld.TypeId()

Case ByteTypeId, ShortTypeId, IntTypeId

fld.SetInt o, Int(innerMarkup)

Case LongTypeId

fld.SetLong o, Long(innerMarkup)

Case FloatTypeId

fld.SetFloat o, Float(innerMarkup)

Case DoubleTypeId

fld.SetFloat o, Double(innerMarkup)

Case StringTypeId

fld.SetString o, XMLRemoveEntities(innerMarkup)

Default

fld.Set o, ConvertXML(innerMarkup, fld.TypeId())

End Select
markup = markup[pos+3+name.length..]

Forever
Local wmth:TMethod = tid.FindMethod("__wakeup")
If wmth Then

wmth.Invoke(o, New Object[0])

EndIf
Return o

End Function

Function XMLToObject:Object(xml$)

If Not xml.StartsWith("<") Then RuntimeError "Error in XML Markup"
Local pos:Int = xml.Find(">")
If pos = -1 Then RuntimeError "Error in XML Markup"
Local name$ = xml[1..pos]
If Not name Then RuntimeError "Error in XML Setup"
Local tid:TTypeId = TTypeId.ForName(name)
If Not tid Then RuntimeError "Could not find Type "+name
If Not xml.EndsWith("</"+name+">") Then RuntimeError "Error in XML Markup"
Local innerMarkup$ = xml[pos+1..xml.length-name.length-3]
Return ConvertXML(innerMarkup, tid)

End Function



Am Ende im Debugger die Variable cp bestaunen.
 

PhillipK

BeitragMi, Dez 26, 2012 7:23
Antworten mit Zitat
Benutzer-Profile anzeigen
Okay, du bist krank o.o

Ich versuche grade, trotz 2 großer Fragezeichen als augaäpfel, nachzuvollziehen, was hier passiert.
Die technik scheint simpel zu sein und könnte auch anderswo brauchbar zu sein, vorrausgesetzt dieses Reflection läust sauber und schnell genug Smile

Nachdem ich einen kleinen fehler gefixt hatte, nähmlich die dreifach-quotes "str = Replace(str, "~q", "~q")" hier zb, wars binnen ner millisekunde fertig und ich konnte cp bewundern.
Einzig das .str feld ist leer. Was ich mir aber damit erkläre, das ich nirgendwo einen zugriff auf selbige finde :3
Ich bin mal basteln *grins*

Danke dir für den code ausschnitt =)

ZEVS

BeitragMi, Dez 26, 2012 12:48
Antworten mit Zitat
Benutzer-Profile anzeigen
Die __sleep-Methode gibt diejenigen Felder zurück, die gespeichert werden sollen. Offensichtlicherweise ist str nicht dabei (es ist demnach keine Fehlfunktion, dass du es nicht gespeichert wurde). Wenn das Objekt z.B. Dateihandles hat, kann es in __sleep anweisen, nur den Dateipfad, nicht aber den Stream zu speichern (was ja reichlich sinnlos wäre) und in __wakeup die Datei wieder öffnen.
Die Funktionen XMLEntities und XMLRemoveEntities sind durch die Forums-Software kaputt gegangen (eigentlich waren hier Entities wie &amp; drin, die aber ersetzt wurden). Ich probiere es nochmal:
BlitzMax: [AUSKLAPPEN]

Function XMLEntities$(str$)
str = Replace(str, "&" , "&amp;" )
str = Replace(str, "<" , "&lt;" )
str = Replace(str, ">" , "&gt;" )
str = Replace(str, "~q", "&quot;")
Return str
End Function

Function XMLRemoveEntities$(str$)
str = Replace(str, "&lt;" , "<" )
str = Replace(str, "&gt;" , ">" )
str = Replace(str, "&quot;", "~q")
str = Replace(str, "&amp;" , "&" )
Return str
End Function



ZEVS
PS: Probiere bitte nicht, Arrays zu speichern. Das habe ich nicht eingebaut.
 

PhillipK

BeitragMi, Dez 26, 2012 13:05
Antworten mit Zitat
Benutzer-Profile anzeigen
Du glaubst doch nicht ernsthaft, das ich deinen code einfach so kopiere und nutze, oder? Smile
So lernt man nix.
Ich bin grade etwas ausgebucht, aber stöbere immer mal wieder die einzelnenen zeilen durch.

Das durch __sleep() die felder zurückgegeben werden, hatte ich geahnt. Aber das __wake da mit zusammenhängt, war mir noch nicht ganz klar. Einzig das adden von str in den string-array gab einen fehler, weshalb ich annahm, das es bei string vielleicht generell probleme gibt.

Ich werde mich nacher wenn ich mehr zeit habe einmal hinsetzen und die scheinbaren reflection-sachen nacharbeiten. Sprich einfach selbst mal printen und schauen, was die methoden so machen, die du aufrufst, dürfte das sinnigste sein ^^

Im moment ist es ein wenig schwer für mich zu unterscheiden, was nun essentiell für den xml baum ist und was essentiell für den reflection part ist. Grundlegend erkenne ich aber, das ich komplette types (sprich so wie sie im quelltext erscheinen) auf stringbasis rekonstruiren kann.
Heißt im umkehr schluß, das die idee mit den abstrakten types wieder ins mögliche rutscht.
unklar ist mir allerdings noch, ob ich auch an sonstige quelltext daten rankommen kann. So gesehen wohl eher weniger, aber dennoch eine sehr intressante möglichkeit die sich da bietet Smile

(danke übrigends für deine mühe, sowas gehört, mit ein paar kommentaren ausgeschmückt, doch direkt ins codearchiv oder alternativ als tutorial umgeschrieben hihi)

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group