Seite 1 von 2

Cross-platform Properties 1.1 (Ersatz für SetGadgetData())

Verfasst: 12.05.2013 02:27
von Lambda
Abend PBBFF´s, :D

SetWindowData, SetMenuItemData, SetImageData, alles was man brauchen könnte mit diesem kleinem Schnipsel. Gerade bei Canvas Elemente - mit SetGadgetData() kann der Nutzer keine eigenen Werte mehr speichern, außer man stelle ein Provisorium dafür bereit.

Es können mehrere Werte zu einer ID eines Types zugeordnet werden. Ein Typ könnte z.B #PB_GadgetType... sein, oder eine eigene Klassifizierung.

Code: Alles auswählen

;======================================================================
; File:            Properties.pbi
;
; Date:            May 14, 2013
; Version:         1.1
; Demo:            No
; Target Compiler: PureBasic 5.1+
; Processor        x32/x64
; Target OS:       Windows, Linux, MacOS
;======================================================================

; More extensive (internal) Surrogate for SetGadgetData()/GetGadgetData(). 
; Can be used for anything Gadget, Item, Menu, StatusBar, Images and so on, or
; custom Types.


; Set to #False to use System Calls.
#PB_ProperiesInternal = #True






CompilerSelect #PB_Compiler_OS
  CompilerCase #PB_OS_Linux
    ImportC "-gtk"
      g_object_set_property_(*Widget, Property.p-utf8, *val) As "g_object_set_property" 
      g_object_get_property_(*Widget, Property.p-utf8) As "g_object_get_property" 
    EndImport
CompilerEndSelect


Structure _PROPERTY
  Map *Prop(32)
EndStructure
Structure _PROPERTYSTOCK
  Map ID._PROPERTY(4096)
EndStructure

Global _PropertyStock._PROPERTYSTOCK



Macro ptDefine(Symbol)
  CompilerIf Defined(Symbol, #PB_Constant) = #False
    #Symbol = MacroExpandedCount
  CompilerEndIf
EndMacro

;[- Property Types

  ptDefine(PB_File)
  ptDefine(PB_Pack)
  ptDefine(PB_Preference)
  ptDefine(PB_Image)
  ptDefine(PB_Movie)
  ptDefine(PB_Font)
  ptDefine(PB_Sound)
  ptDefine(PB_Sound3D)
  ptDefine(PB_Sprite)
  ptDefine(PB_Sprite3D)
  ptDefine(PB_Texture)
  ptDefine(PB_Mesh)
  ptDefine(PB_StaticGeometry)
  ptDefine(PB_Entity)
  ptDefine(PB_Spline)
  ptDefine(PB_Material)
  ptDefine(PB_Light)
  ptDefine(PB_Joint)
  ptDefine(PB_Node)
  ptDefine(PB_NodeAnimation)
  ptDefine(PB_Effect)
  ptDefine(PB_Billboard)
  ptDefine(PB_Terrain)
  ptDefine(PB_Particle)
  ptDefine(PB_Expression)
  ptDefine(PB_Module)
  ptDefine(PB_Network)
  ptDefine(PB_Thread)
  ptDefine(PB_Process)
  ptDefine(PB_Printer)
  
  ptDefine(PB_XML)
  ptDefine(PB_Palette)
  ptDefine(PB_Mail)
  ptDefine(PB_Joystick)
  ptDefine(PB_Keyboard)
  ptDefine(PB_Mouse)
  
  ptDefine(PB_Gadget3D)
  ptDefine(PB_GadgetItem3D)
  ptDefine(PB_Window3D)
  ptDefine(PB_Control) ; Canvas based Controls
  ptDefine(PB_Gadget)
  ptDefine(PB_GadgetItem)
  ptDefine(PB_Window)
  ptDefine(PB_Menu)
  ptDefine(PB_MenuItem)
  ptDefine(PB_ToolBar)
  ptDefine(PB_ToolBarItem)
  ptDefine(PB_Popup)
  ptDefine(PB_StatusBar)
  ptDefine(PB_StatusBarField)
  ptDefine(PB_SysTray)
  
  ; User defined
  
  
;]-
  


Procedure SetProp(ID, Name.s, *Data, Type = #Null)
  CompilerIf #PB_ProperiesInternal = #True
    _PropertyStock\ID(Str(ID))\Prop(Str(Type)+"\"+Name) = *Data
  CompilerElse
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        SetProp_(ID, Name, *Data)
      CompilerCase #PB_OS_Linux
        g_object_set_property_(Handle, Property, *Data)
      CompilerCase #PB_OS_MacOS
    CompilerEndSelect 
  CompilerEndIf
EndProcedure

Procedure GetProp(ID, Name.s, Type = #Null)
  CompilerIf #PB_ProperiesInternal = #True
    ProcedureReturn _PropertyStock\ID(Str(ID))\Prop(Str(Type)+"\"+Name)
  CompilerElse
    CompilerSelect #PB_Compiler_OS
      CompilerCase #PB_OS_Windows
        ProcedureReturn GetProp_(ID, Name)
      CompilerCase #PB_OS_Linux
        ProcedureReturn g_object_get_property_(ID, Name)
      CompilerCase #PB_OS_MacOS
    CompilerEndSelect 
  CompilerEndIf
EndProcedure

Procedure RemoveProp(Type, ID, Name)
CompilerIf #PB_ProperiesInternal = #True
  DeleteMapElement( _PropertyStock\ID(Str(ID))\Prop(), Str(Type)+"\"+Name )
CompilerElse
  CompilerSelect #PB_Compiler_OS
    CompilerCase #PB_OS_Windows
      RemoveProp_(ID, Name)
    CompilerCase #PB_OS_Linux
      ; Unknown
    CompilerCase #PB_OS_MacOS
  CompilerEndSelect 
CompilerEndIf
EndProcedure


;- Extra
CompilerIf #PB_ProperiesInternal = #True
  Procedure IsProp(Type, ID, Name)
    
    If FindMapElement( _PropertyStock\ID(Str(ID))\Prop(), Str(Type)+"\"+Name )
      ProcedureReturn #True
    Else
      ProcedureReturn #False
    EndIf
    
  EndProcedure
  
  Procedure CollectProp(ID, List *pData(), Type)
    ClearList(*pData())
    ForEach _PropertyStock\ID(Str(ID))\Prop()
      AddElement(*pData())
      *pData() = _PropertyStock\ID(Str(ID))\Prop()
    Next
    ResetList(*pData())
  EndProcedure
CompilerEndIf


;- Test
CompilerIf #PB_Compiler_IsMainFile
  OpenWindow(0, 0, 0, 200, 200, "Internal Properties", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  CanvasGadget(0, 10, 10, 180, 180)
  
  SetProp(0, "MyData", 16, #PB_Control)
  SetProp(0, "OtherData", 32, #PB_Control)
  
  Define NewList All()
  
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_CloseWindow
        End
        
      Case #PB_Event_Gadget
        If EventType() = #PB_EventType_LeftButtonDown
          
          CollectProp(0, @All(), #PB_Control)
          
          ForEach All()
            Debug All()
          Next
        EndIf
        
    EndSelect
  ForEver
CompilerEndIf

Re: Cross-platform Properties (Ersatz für SetGadgetData())

Verfasst: 12.05.2013 03:05
von STARGÅTE
Hm, da wäre es aber ganz nett, wenn du noch die Konstaten für die Libs zusammenstellst.
Du hast ja hier ein Beispiel mit GadgetType gemacht, deren Konstanten von 1 bis 33 oder so gehen.
Wenn nun jeder selber seine Konstanten für die Libs festlegen muss, gibts bestimmt Probleme.
Da es ja keine gleichen IDs bei Gadgets geben kann, würde es ja auch dort reichen nur einen Typ zu nutzen, und dann:

Code: Alles auswählen

Enumeration
	#PB_Image
	#PB_Gadget
	#PB_GadgetItem
	#PB_Window
	#PB_Menu
	#PB_MenuItem
	;...
EndEnumeration
Wie du hier siehst, wäre also ein zusätzlicher Parameter noch interessant: Item
Allerdings solltest du mit Maps vorsichtig sein, ID -> Type -> Name und dann noch -> Item wären schon 4 geschachtelte Maps.
Standardmäßig haben diese jeweils 512 Slots -> 4kB pro Map -> 16kB pro ID-Element.

Das klingt nicht viel, sollte man aber trotzdem im Hinterkopf behalten.
Gegebenenfalls wäre hier vllt eine einzige Map, deren Hashs zB aus Str(ID)+","+Str(Type)+","+Name besteht effektiver (ungetestet).

Was noch bei ExamineProp() wichtig ist: Vorab ein ClearList(*pData()) und hinten ein ResetList(*pData())

Ideen:
  • IsProb() mit FindMapElement(), um zu testen, ob Prob überhaupt gesetzt wurde (wenn zB auch 0 ein normaler Wert wäre).
  • FreeProb(), damit man keine Speicherlecks bekommt, wenn man dynamisch Gadgets erstellt/freigibt.

Re: Cross-platform Properties (Ersatz für SetGadgetData())

Verfasst: 12.05.2013 03:21
von Lambda
Mit der Nummerierung wäre es als Fundament sehr gut, um die PB internen abzudecken - alles darüber wäre benutzerdefiniert.

Die Zusammenführung von ID und Type wäre so möglich, aber mit Verkettung wirklich effektiver? Die Slots könnten zu Beginn niedriger gesetzt werden. FreeProp() habe ich allerdings vergessen. :D

Grundlegend soll das Konzept zumindest den Eigenbedarf decken um Kollisionen zu vermeiden. Anders als SetProp_() ist dies ja intern und nicht System weit. :D

Re: Cross-platform Properties (Ersatz für SetGadgetData())

Verfasst: 12.05.2013 09:15
von Bisonte
Die Idee ist faszinierend.

Wenn sich die OS-Handles bei Linux und Mac so verhalten wie bei Windows (also alle einzigartig, egal ob Gadget,Window,Tool-, und StatusBar),
dann könnte man das doch eher an die Windowssyntax und funktionsweise anpassen ....

Ich meine in etwa so...

Code: Alles auswählen

Structure struct_properties
  
  hWnd.i
  Map Property.i()
  
EndStructure

Global NewList Properties.struct_properties()

Procedure SetProp(hWnd, Property$, Value)
  
  Protected Result = #False
  
  If hWnd
    ForEach Properties()
      If Properties()\hWnd = hWnd
        Properties()\Property(Property$) = Value
        Result = #True
        Break  
      EndIf
    Next
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
Procedure GetProp(hWnd, Property$)
  
  Protected Result = #False
  
  If hWnd
    ForEach Properties()
      If Properties()\hWnd = hWnd
        If MapKey(Properties()\Property()) = Property$
          Result = Properties()\Property(Property$)
          Break
        EndIf
      EndIf
    Next
  EndIf
  
  ProcedureReturn Result
  
EndProcedure
Procedure RemoveProp(hWnd, Property$)
  
  Protected Result = #False
  
  If hWnd
    ForEach Properties()
      If Properties()\hWnd = hWnd    
        If MapKey(Properties()\Property()) = Property$
          DeleteMapElement(Properties()\Property(), Property$)
          Result = #True
          Break
        EndIf
      EndIf
    Next  
  EndIf
    
  ProcedureReturn Result
  
EndProcedure
Procedure ClearProbs(hWnd)
  
  Protected Result = #False
  
  ForEach Properties()
    If Properties()\hWnd = hWnd
      ClearMap(Properties()\Property()) 
      FreeMap(Properties()\Property())
      DeleteElement(Properties())
      Result = #True
      Break
    EndIf
  Next
  
  ProcedureReturn Result
  
EndProcedure
Aber ich schätze, wenn es sich um grössere GUI-Projekte handelt (also massenhaft Gadgets und Windows etc.), dürfte diese Methode
zu langsam sein, als das man sie z.B. in einem Callback anwendet.

Re: Cross-platform Properties 1.1 (Ersatz für SetGadgetData(

Verfasst: 14.05.2013 10:42
von Lambda
Eine verbesserte Version. Einige PB Typen wurden hinzugefügt, mit ptDefine(Symbol) können weitere Konstanten festgelegt werden. Setzt man #PB_PropertiesInternal auf #False werden die System Befehle genutzt (aktuell für Windows und Linux, MacOS ist doof).

Die Struktur wurde auf 2 reduziert, mit der internen Lösung gibt es IsProp und RemoveProp. Für Linux fand ich aber keine für RemoveProp. :?

Re: Cross-platform Properties 1.1 (Ersatz für SetGadgetData(

Verfasst: 14.05.2013 12:28
von STARGÅTE
Na das ist ja hier quatsch:

Code: Alles auswählen

Structure _PROPERTY
  Map *Prop(1)
EndStructure
Structure _PROPERTYSTOCK
  Map ID._PROPERTY(1)
EndStructure
Mit einem Slot, machst du ja den Vorteil einer Map zu nichte, da ja nun alle Elemente den selben Hash erhalten und damit immer geprüft werden muss welches Element das richtige ist.

Da _PROPERTYSTOCK nur einmal vorkommt, kannst du dort sogar mehr Slots anbieten, damit die Funktion bei vielen IDs noch schneller wird. _PROPERTY wird hingegen öfter erstellt, dort kannst du die Slots auf 32 oder so setzen, mehr sollte bei ein und der selben ID nicht nötig sein.

Code: Alles auswählen

Structure _PROPERTY
  Map *Prop(32)
EndStructure
Structure _PROPERTYSTOCK
  Map ID._PROPERTY(4096)
EndStructure

Re: Cross-platform Properties 1.1 (Ersatz für SetGadgetData(

Verfasst: 14.05.2013 13:07
von Lambda
Arrr, ich bin ein Pirat. :D Das wurde eigentlich gänzlich entfernt, aber übernehme mal deinen Vorschlag. :D

Re: Cross-platform Properties (Ersatz für SetGadgetData())

Verfasst: 14.05.2013 13:59
von Lambda
Bisonte hat geschrieben:Wenn sich die OS-Handles bei Linux und Mac so verhalten wie bei Windows (also alle einzigartig, egal ob Gadget,Window,Tool-, und StatusBar),
Zwischen Windows und Linux gibt es äquivalente API Befehle, wie ich sie in der neue Version hinzugefügt hab. MacOS habe ich noch nicht recherchiert. Aber unter Linux finde ich bspw. kein "RemoveProp" auf die schnelle - existiert sicherlich.

Re: Cross-platform Properties 1.1 (Ersatz für SetGadgetData(

Verfasst: 14.05.2013 14:41
von Bisonte
Das "RemoveProp_()" unter Windows finde ich eigentlich vernachlässigbar.

Warum soll ich etwas entfernen, wenn der Träger sowieso gelöscht wird.
Ich mache dies zwar (wegen dem Stil, alles was ich erstelle, gebe ich auch wieder frei), aber sinnfrei wie ich finde.

Evt. dachten sich die Macher von Linux das gleiche ;)

Aber mal so nebenbei... Da sich, zumindest unter Linux und Windows, die Properties gleich verhalten, wieso machst du es dann
unnötig kompliziert ?
Jedes Objekt mit einem einzigartigen OS-Handle kann beliebig viele Properties haben, zumindest wenn ich das alles so richtig verstanden habe.
Dann braucht man im Grunde keine Map, List oder sonstige Speicherung, weil das Objekt selbst doch schon die Speicherung wäre...
Oder bin ich da komplett auf dem falschen Dampfer ?

Re: Cross-platform Properties 1.1 (Ersatz für SetGadgetData(

Verfasst: 14.05.2013 16:49
von Lambda
Beim Thema RemoveProp gebe ich dir im Prinzip recht, kann mir direkt kein Szenario vorstellen bei dem es benötigt wäre - für die interne Lösung allerdings benötigt, da nicht direkt daran gebunden.
Aber mal so nebenbei... Da sich, zumindest unter Linux und Windows, die Properties gleich verhalten, wieso machst du es dann
unnötig kompliziert ?
Für Gadgets und Windows ja, aber mit der Klassifizierung lassen sich auch Eigenschaften auf beliebige Objekte zuordnen wie ToolBar, ToolBar Elemente, SysTray, Gadget Item, Image etc. Ob man das benötigt liegt im eigenem Ermessen, wenn nicht - #PB_PropertiesInternal = #False. :wink: Für MacOS habe ich allerdings noch keine äquivalente Funktionen!