Mein Eintrag ist ein Code-Schnippsel und einige Fragen in einem!
Nachdem ich ja mein Skriptsprachentutorial (siehe Signatur) begonnen habe, bekam ich wieder Lust, eine Adventure-Engine anzufangen (die 543. nicht vollendete, denke ich
Dazu brauche ich aber Tree Gadgets für einen Projekt Tree a la Quest:

Da ich mit dem Tree Gadget schon einmal nur Ärger hatte (habe das damals mit einer Linked List gelöst, mit der ich dann den Tree jedesmal neu befüllt habe bei einer Änderung), wollte ich es jetzt besser machen.
Also habe ich mich an eine Designstudie gesetzt (die mich furchtbar geärgert hat und 3 Tage gekostet hat
Besonderes Augenmerk möchte ich auf 5 Dinge lenken:
- CreateTreeItem() wollte ewig nicht ordentlich funktionieren, wenn das Element 0 schon ein Child-Node hatte (Level 1), dann war es nicht mehr möglich, ein neues Item auf dem 0-Level einzufügen. Konsequent erschien es im Child Node des Element 0. Nur durch Herumprobieren habe ich es hingekriegt. Warum es so funktioniert, verstehe ich nicht! Bitte um Hilfe und Erklärungen!
- Modul-Methoden für das Löschen und Umbenennen sind nicht notwendig, weil das das Gadget nativ toll hinkriegt.
- Ich habe eine kleine Möglichkeit eingebaut, die Trees in Textdateien zu laden und aus ihnen wieder zu speichern (Beispieldatei liegt bei.
- Meine Methode GetParentOfTreeItem(), mit der ich die Nummer des Parent Nodes eines Nodes ermittle, muss doch auch ohne Schleife mittels API gehen? Oder? Bitte um Tipps durch die API-Könner!
- In der Debug-Ausgabe feuert das Event 2-mal, was man auch am doppelten Text für das angeklickte Node erkennt. Kann man das verhindern, bzw. welche 2 Events sind das?
Das Modul Tree:
Code: Alles auswählen
EnableExplicit
; *******************************************************************
; * Konzept für ein Module TREE GADGET
; * (c)2014 Puretom, viel Spaß damit
; *******************************************************************
DeclareModule Tree
Declare SaveTreeToDisk(file_ID,path.s, tree_gadget_ID)
Declare LoadTreeFromDisk(file_ID, path.s, tree_gadget_ID)
Declare GetParentOfTreeItem(tree_gadget_ID, item_nr)
Declare CreateTreeItem(tree_gadget_ID, item_text.s, click_selected_item_nr, sub_item )
EndDeclareModule
Module Tree
EnableExplicit
; File System
;
Procedure SaveTreeToDisk(file_ID,path.s, tree_gadget_ID)
Protected item_nr, max_items
; File öffnen,
CreateFile(file_ID, path)
; Speichert folgende Daten in ein File als Strings:
; 1. Text des Gadget Nodes
; 2. Sublevel-Nummer des Gadget Nodes
; Anmerkung: Man könnte 2 auch mit z.B WriteAsciiCharacter o.Ä.
; speichern, aber zum besseren Herzeigen, hier als Text
max_items = CountGadgetItems(tree_gadget_ID)-1
For item_nr = 0 To max_items
WriteStringN(file_ID, GetGadgetItemText(tree_gadget_ID, item_nr))
WriteStringN(file_ID, Str(GetGadgetItemAttribute(tree_gadget_ID, item_nr, #PB_Tree_SubLevel )))
Next
CloseFile(file_ID)
EndProcedure
Procedure LoadTreeFromDisk(file_ID, path.s, tree_gadget_ID)
Protected item_nr
Protected node_name.s, node_type, node_sublevel
ClearGadgetItems(tree_gadget_ID)
item_nr=0
ReadFile(file_ID, path)
While Eof(0) = 0
; Daten aus Datei laden
node_name.s = ReadString(file_ID)
node_sublevel= Val(ReadString(file_ID))
; Tree Node erzeugen
; hier könnte man gleich das Image dazugeben -------.
; wenn man nicht modular bleiben möchte |
; v
AddGadgetItem(tree_gadget_ID, item_nr, node_name, 0, node_sublevel)
; Next Item Nr
item_nr+1
Wend
CloseFile(file_ID)
EndProcedure
; Parent eines Items ermitteln
; ( das muss doch auch mit API und ohne Schleife gehen? Oder ? )
Procedure GetParentOfTreeItem(tree_gadget_ID, item_nr)
Protected sub_level
sub_level=GetGadgetItemAttribute(tree_gadget_ID, item_nr, #PB_Tree_SubLevel)
If sub_level<>0
Repeat
item_nr-1
Until sub_level>GetGadgetItemAttribute(tree_gadget_ID, item_nr, #PB_Tree_SubLevel)
Else
item_nr=-1
EndIf
ProcedureReturn item_nr
EndProcedure
; Create Tree Item
;
Procedure CreateTreeItem(tree_gadget_ID, item_text.s, click_selected_item_nr, sub_item )
Protected new_item_nr, sub_level
; Falls es sich um ein Unter-Item handelt: sub_item=1, sonst sub_item=0
; das Sub-Level wird GRÖSSER, wenn Unter-Level
sub_level = GetGadgetItemAttribute(tree_gadget_ID, click_selected_item_nr, #PB_Tree_SubLevel) + sub_item
; ??? Eine sehr seltsame Verhaltensweise, die ich durch reines Probieren herausgefunden habe ???
; ??? Keine Ahnung, warum das so sein muss ???
new_item_nr = click_selected_item_nr
If sub_item=1: new_item_nr+1: EndIf
; anlegen
AddGadgetItem(tree_gadget_ID, new_item_nr, item_text, 0, sub_level )
SetGadgetItemState(tree_gadget_ID, click_selected_item_nr, #PB_Tree_Expanded )
EndProcedure
EndModule
Code: Alles auswählen
Enumeration FormWindow
#Window_TestTreeGadgetModule
EndEnumeration
Enumeration FormGadget
#Tree_Project
#Editor_Explains
#Container_0
#Button_Loeschen
#Button_Umbenennen
#Button_Debug_Anzeige_Loeschen
#Button_NeuesUnterItem
#Button_LoadTree
#Button_SaveTree
#Button_NeuesItem
EndEnumeration
Enumeration FormFont
#Font_Window_TestTreeGadgetModule_0
EndEnumeration
LoadFont(#Font_Window_TestTreeGadgetModule_0,"Courier", 8)
Declare ResizeGadgetsWindow_TestTreeGadgetModule()
Declare Button_LoadTree(EventType)
Declare Tree_Project(EventType)
Declare Button_Debug_Anzeige_Loeschen(EventType)
Declare Button_NeuesUnterItem(EventType)
Declare Button_Umbenennen(EventType)
Declare Button_NeuesItem(EventType)
Declare Button_Loeschen(EventType)
Declare Button_SaveTree(EventType)
Procedure OpenWindow_TestTreeGadgetModule(x = 0, y = 0, width = 655, height = 635)
OpenWindow(#Window_TestTreeGadgetModule, x, y, width, height, "Pure Board Tree Gadget Test Window", #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SizeGadget | #PB_Window_ScreenCentered)
TreeGadget(#Tree_Project, 0, 0, 370, 460)
EditorGadget(#Editor_Explains, 370, 0, 285, 635, #PB_Editor_WordWrap)
SetGadgetFont(#Editor_Explains, FontID(#Font_Window_TestTreeGadgetModule_0))
ContainerGadget(#Container_0, 0, 460, 370, 175)
ButtonGadget(#Button_Loeschen, 235, 145, 125, 22, "Item löschen")
ButtonGadget(#Button_Umbenennen, 235, 105, 125, 22, "Item umbenennen")
ButtonGadget(#Button_Debug_Anzeige_Loeschen, 30, 10, 300, 22, "Debuganzeige löschen")
GadgetToolTip(#Button_Debug_Anzeige_Loeschen, "Löscht den Editor, der als Debugausgabe dient.")
ButtonGadget(#Button_NeuesUnterItem, 235, 75, 125, 22, "Neues Unter-Item")
GadgetToolTip(#Button_NeuesUnterItem, "Legt auf einem Level über dem selektierten Element ein Item an.")
ButtonGadget(#Button_LoadTree, 10, 110, 95, 22, "Tree laden")
GadgetToolTip(#Button_LoadTree, "Lädt tree von einem Texfile")
ButtonGadget(#Button_SaveTree, 10, 80, 95, 22, "Tree speichern")
GadgetToolTip(#Button_SaveTree, "Speichert den Tree in ein Textfile")
ButtonGadget(#Button_NeuesItem, 235, 50, 125, 22, "Neues Item")
GadgetToolTip(#Button_NeuesItem, "Legt auf demselben Level wie das selektierte Element ein Item an.")
CloseGadgetList()
EndProcedure
Procedure ResizeGadgetsWindow_TestTreeGadgetModule()
Protected FormWindowWidth, FormWindowHeight
FormWindowWidth = WindowWidth(#Window_TestTreeGadgetModule)
FormWindowHeight = WindowHeight(#Window_TestTreeGadgetModule)
ResizeGadget(#Tree_Project, 0, 0, 370, FormWindowHeight - 175)
ResizeGadget(#Editor_Explains, 370, 0, FormWindowWidth - 370, FormWindowHeight - 0)
ResizeGadget(#Container_0, 0, FormWindowHeight - 175, 370, 175)
ResizeGadget(#Button_Loeschen, 235, 145, 125, GadgetHeight(#Container_0) - 153)
ResizeGadget(#Button_Umbenennen, 235, 105, 125, GadgetHeight(#Container_0) - 153)
ResizeGadget(#Button_Debug_Anzeige_Loeschen, 30, 10, 300, GadgetHeight(#Container_0) - 153)
ResizeGadget(#Button_NeuesUnterItem, 235, 75, 125, GadgetHeight(#Container_0) - 153)
ResizeGadget(#Button_LoadTree, 10, 110, 95, GadgetHeight(#Container_0) - 153)
ResizeGadget(#Button_SaveTree, 10, 80, 95, GadgetHeight(#Container_0) - 153)
ResizeGadget(#Button_NeuesItem, 235, 50, 125, GadgetHeight(#Container_0) - 153)
EndProcedure
Procedure Window_TestTreeGadgetModule_Events(event)
Select event
Case #PB_Event_SizeWindow
ResizeGadgetsWindow_TestTreeGadgetModule()
Case #PB_Event_CloseWindow
ProcedureReturn #False
Case #PB_Event_Menu
Select EventMenu()
EndSelect
Case #PB_Event_Gadget
Select EventGadget()
Case #Tree_Project
Tree_Project(EventType())
Case #Button_Loeschen
Button_Loeschen(EventType())
Case #Button_Umbenennen
Button_Umbenennen(EventType())
Case #Button_Debug_Anzeige_Loeschen
Button_Debug_Anzeige_Loeschen(EventType())
Case #Button_NeuesUnterItem
Button_NeuesUnterItem(EventType())
Case #Button_LoadTree
Button_LoadTree(EventType())
Case #Button_SaveTree
Button_SaveTree(EventType())
Case #Button_NeuesItem
Button_NeuesItem(EventType())
EndSelect
EndSelect
ProcedureReturn #True
EndProcedure
Code: Alles auswählen
; *******************************************************************
; * Beispielprogramm für die Nutzung des Moduls TREE GADGET
; * (c)2014 Puretom
; *******************************************************************
XIncludeFile "Pure Board Tree Gadget (Form).pbf"
; Handler-Routinen für die Buttons
;
Procedure Button_NeuesItem(EventType)
Protected item_text.s, click_selected_item_nr
; Daten aus dem Tree holen
; holt item_nr des aktuell geklickten Items
click_selected_item_nr = GetGadgetState(#Tree_Project)
; Item anlegen
item_text= InputRequester("Namen eingeben",
"Geben Sie den Namen des neuen Items ein:","")
If item_text<>""
Tree::CreateTreeItem(#Tree_Project, item_text,
click_selected_item_nr, 0)
EndIf
EndProcedure
Procedure Button_NeuesUnterItem(EventType)
Protected item_text.s, click_selected_item_nr
; Daten aus dem Tree holen
; holt item_nr des aktuell geklickten Items
click_selected_item_nr = GetGadgetState(#Tree_Project)
; Item anlegen
item_text= InputRequester("Namen eingeben",
"Geben Sie den Namen des neuen Items ein:","")
If item_text<>""
Tree::CreateTreeItem(#Tree_Project, item_text,
click_selected_item_nr, 1)
EndIf
EndProcedure
Procedure Button_SaveTree(EventType)
Protected path.s
path=SaveFileRequester("Tree laden",GetCurrentDirectory(),"Text-Dateien (*.txt) | *.txt",0)
If path<>""
Tree::SaveTreeToDisk(0,path, #Tree_Project)
EndIf
EndProcedure
Procedure Button_LoadTree(EventType)
Protected path.s
path=OpenFileRequester("Tree laden",GetCurrentDirectory(),"Text-Dateien (*.txt) | *.txt",0)
If path<>""
Tree::LoadTreeFromDisk(0,path, #Tree_Project)
EndIf
EndProcedure
Procedure Button_Debug_Anzeige_Loeschen(EventType)
SetGadgetText(#Editor_Explains,"")
EndProcedure
Procedure Button_Umbenennen(EventType)
Protected item_text.s
; Item anlegen
item_text= InputRequester("Namen eingeben",
"Geben Sie den Namen des neuen Items ein:","")
If item_text<>""
SetGadgetText(#Tree_Project,item_text)
EndIf
EndProcedure
Procedure Button_Loeschen(EventType)
Protected item_nr
item_nr=GetGadgetState(#Tree_Project)
RemoveGadgetItem(#Tree_Project, item_nr)
EndProcedure
; Event des Tree Gadgets (hier, irgendein Event, z.B. Click)
; trägt Infos in den Debug-Editor ein
;
Procedure Tree_Project(EventType)
AddGadgetItem(#Editor_Explains, -1,
LSet("select: "+GetGadgetText(#Tree_Project),35," ")+
LSet("| Node: " +Str(GetGadgetState(#Tree_Project)),15," ")+
LSet("| Level: "+Str(GetGadgetItemAttribute(#Tree_Project,
GetGadgetState(#Tree_Project),
#PB_Tree_SubLevel)),15," ")+
"| Parent: "+Str(Tree::GetParentOfTreeItem(#Tree_Project,
GetGadgetState(#Tree_Project))))
EndProcedure
; Start und Event-Schleife des Test-Programms
;
Procedure RunTestProgram()
Protected event, text$
; Öffne Window und setze Erklärungen ein
OpenWindow_TestTreeGadgetModule()
; Event-Schleife
Repeat
event = WaitWindowEvent()
Window_TestTreeGadgetModule_Events(event)
Until event = #PB_Event_CloseWindow
EndProcedure
RunTestProgram()
Ein Beispiel Tree, einfach als eine Textdatei speichern und mit dem Laden-Knopf laden:
Code: Alles auswählen
Objects
0
game
1
Verbs
2
Verb: answer
3
Commands
2
Dark corridor
1
player
2
Shirt
3
Cord trousers
3
Black Shoes
3
cell phone
1
Functions
0
Timers
0
cell phone call 1
1
ringing phone stopper
1
Walkthrough
0
Advanced
0
Settings
1
Also, viel Spaß damit!
Und ich bitte um Verbesserungsvorschläge und Tipps zum Umgang mit Trees (siehe markierte Fragen oben), denn so ganz im Griff hatte ich die Sache nicht.
LG Puretom!
Edit: Kleiner Bug in GetParentOfTreeItem() ausgebessert
Code: Alles auswählen
Else
item_nr=-1
Code: Alles auswählen
Else
item_nr=0