Page 1 of 1

Associate named properties with gadgets (requires PB 4.50)

Posted: Thu May 06, 2010 3:50 pm
by freak
PB provides Set/GetGadgetData() to associate a single custom value with any PB gadget. This code shows a simple way to extend this to store any number of named properties in a gadget using the 4.50 maps in structure feature. This mimics the Windows API Set/GetProp_() functions, but it works on all platforms. It is also easy to modify to store other things like strings in these properties.

When you free a gadget with properties on it, call RemoveAllProps() first to clean up the associated memory as well. You can also just call ClearList(AllPropMaps()) to clean up everything, but then the pointers stored in SetGadgetData() will be wrong, so only do it when you destroy all gadgets as well.

Code: Select all

; Structure to store the property map. You can easily modify this to store other stuff than just an integer value
; A pointer to this structure is stored with Set/GetGadgetData() for every gadget that you set properties on
;
Structure PropMap
  Map Props.i()
EndStructure

; Store all the Map structures in a global list. This way a simple ClearList() cleans everything up, and
; there is no need to worry about things like Initialize/ClearStructure() which we would have to do for
; allocated memory blocks
;
Global NewList AllPropMaps.PropMap()


; Helper function:
;   Returns the PropMap structure for #Gadget, Create specifies wether or not to create
;   one if none exists.
;
Procedure GetPropMap(Gadget, Create)
  *PropMap.PropMap = GetGadgetData(Gadget)
  If *PropMap = 0 And Create
    AddElement(AllPropMaps())    
    *PropMap = @AllPropMaps()
    SetGadgetData(Gadget, *PropMap)
  EndIf
  ProcedureReturn *PropMap
EndProcedure

; Set a property on the given #Gadget
;
Procedure SetProp(Gadget, Name$, Value)
  *PropMap.PropMap = GetPropMap(Gadget, #True)
  *PropMap\Props(Name$) = Value
EndProcedure

; Get a property from the given #Gadget. Returns 0 if the property does not exist
;
Procedure GetProp(Gadget, Name$)
  *PropMap.PropMap = GetPropMap(Gadget, #False)
  If *PropMap
    ProcedureReturn *PropMap\Props(Name$)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

; Remove a peoperty from the given #Gadget (if it exists)
;
Procedure RemoveProp(Gadget, Name$)
  *PropMap.PropMap = GetPropMap(Gadget, #False)
  If *PropMap
    DeleteMapElement(*PropMap\Props(), Name$)
  EndIf
EndProcedure

; Remove all properties (and the map) from a given #Gadget.
; This should be called before FreeGadget() to make sure there are no leaks
;
Procedure RemoveAllProps(Gadget)
  *PropMap.PropMap = GetPropMap(Gadget, #False)
  If *PropMap
    ChangeCurrentElement(AllPropMaps(), *PropMap)
    DeleteElement(AllPropMaps())
    SetGadgetData(Gadget, 0)
  EndIf
EndProcedure




; Example
;
Enumeration
  #Spin1
  #Spin2
  #Text1
  #Text2
EndEnumeration


If OpenWindow(0, 0, 0, 210, 100, "SpinGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)  
  SpinGadget(#Spin1, 20, 20, 80, 25, 0, 100, #PB_Spin_Numeric)
  SpinGadget(#Spin2, 20, 55, 80, 25, 0, 100, #PB_Spin_Numeric)
  TextGadget(#Text1, 110, 20, 80, 25, "", #PB_Text_Border)
  TextGadget(#Text2, 110, 55, 80, 25, "", #PB_Text_Border)
  
  SetProp(#Spin1, "TextGadget", #Text1)
  SetProp(#Spin1, "Multiplier", 5)
  SetGadgetState(#Spin1, 0)
  
  SetProp(#Spin2, "TextGadget", #Text2)
  SetProp(#Spin2, "Multiplier", 10)
  SetGadgetState(#Spin2, 0)
  
  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_Gadget
      Gadget = EventGadget()
      SetGadgetText(GetProp(Gadget, "TextGadget"), Str(GetGadgetState(Gadget) * GetProp(Gadget, "Multiplier")))      
    EndIf
  Until Event = #PB_Event_CloseWindow
EndIf

Re: Associate named properties with gadgets (requires PB 4.5

Posted: Sat May 08, 2010 10:18 pm
by Tranquil
Good example that shows how to use maps and what kind of circumstances they are useful for. Thanks for posting!

Re: Associate named properties with gadgets (requires PB 4.5

Posted: Sun May 09, 2010 10:09 am
by SFSxOI
Hurry up and relase PB 4.5 I wanna use mapping in some projects for work :) (I can't use the beta for work projects due to employer restrictions), but I am collecting the snippets for use when 4.5 is released. Thanks for sharing this.

Re: Associate named properties with gadgets (requires PB 4.5

Posted: Sun May 09, 2010 10:39 am
by Fred
You can also use a global map instead of a list, and you don't need to rely on pointers and Get/SetGadgetData():

Code: Select all

; Structure to store the property map. You can easily modify this to store other stuff than just an integer value
; A pointer to this structure is stored with Set/GetGadgetData() for every gadget that you set properties on
;
Structure PropMap
  Map Props.i()
EndStructure

; Store all the Map structures in a global map. This way a simple ClearMap() cleans everything up, and
; there is no need to worry about things like Initialize/ClearStructure() which we would have to do for
; allocated memory blocks
;
Global NewMap AllPropMaps.PropMap()


; Set a property on the given #Gadget
;
Procedure SetProp(Gadget, Name$, Value)
  AllPropMaps(Str(Gadget))\Props(Name$) = Value
EndProcedure

; Get a property from the given #Gadget. Returns 0 if the property does not exist
;
Procedure GetProp(Gadget, Name$)
  If AllPropMaps(Str(Gadget))
    ProcedureReturn AllPropMaps()\Props(Name$)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

; Remove a property from the given #Gadget (if it exists)
;
Procedure RemoveProp(Gadget, Name$)
  If AllPropMaps(Str(Gadget))
    DeleteMapElement(AllPropMaps()\Props(), Name$)
  EndIf
EndProcedure

; Remove all properties (and the map) from a given #Gadget.
; This should be called before FreeGadget() to make sure there are no leaks
;
Procedure RemoveAllProps(Gadget)
  DeleteMapElement(AllPropMaps(), Str(Gadget))
EndProcedure


; Example
;
Enumeration
  #Spin1
  #Spin2
  #Text1
  #Text2
EndEnumeration


If OpenWindow(0, 0, 0, 210, 100, "SpinGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered) 
  SpinGadget(#Spin1, 20, 20, 80, 25, 0, 100, #PB_Spin_Numeric)
  SpinGadget(#Spin2, 20, 55, 80, 25, 0, 100, #PB_Spin_Numeric)
  TextGadget(#Text1, 110, 20, 80, 25, "", #PB_Text_Border)
  TextGadget(#Text2, 110, 55, 80, 25, "", #PB_Text_Border)
 
  SetProp(#Spin1, "TextGadget", #Text1)
  SetProp(#Spin1, "Multiplier", 5)
  SetGadgetState(#Spin1, 0)
 
  SetProp(#Spin2, "TextGadget", #Text2)
  SetProp(#Spin2, "Multiplier", 10)
  SetGadgetState(#Spin2, 0)
 
  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_Gadget
      Gadget = EventGadget()
      SetGadgetText(GetProp(Gadget, "TextGadget"), Str(GetGadgetState(Gadget) * GetProp(Gadget, "Multiplier")))     
    EndIf
  Until Event = #PB_Event_CloseWindow
EndIf

Re: Associate named properties with gadgets (requires PB 4.5

Posted: Sun May 09, 2010 11:04 am
by luis
SFSxOI wrote:Hurry up and relase PB 4.5 I wanna use mapping in some projects for work :)
A hurried beta is a final with the bugs of a beta.

Re: Associate named properties with gadgets (requires PB 4.5

Posted: Sun May 09, 2010 6:00 pm
by mesozorn
Also possible to do it very simply without any structures or procedures whatsoever, as per my example in another thread:
http://www.purebasic.fr/english/viewtop ... 09#p323609

Code: Select all

Global NewMap Properties.l()

Macro SetProp(gadget,prop,value)
  Properties(Str(gadget)+"_"+prop)=value
EndMacro

Macro GetProp(gadget,prop)
  Properties(Str(gadget)+"_"+prop)
EndMacro

Macro RemoveProp(gadget,prop)
  DeleteMapElement(Properties(),Str(gadget)+"_"+prop)
EndMacro


; Example
;
Enumeration
  #Spin1
  #Spin2
  #Text1
  #Text2
EndEnumeration


If OpenWindow(0, 0, 0, 210, 100, "SpinGadget", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  SpinGadget(#Spin1, 20, 20, 80, 25, 0, 100, #PB_Spin_Numeric)
  SpinGadget(#Spin2, 20, 55, 80, 25, 0, 100, #PB_Spin_Numeric)
  TextGadget(#Text1, 110, 20, 80, 25, "", #PB_Text_Border)
  TextGadget(#Text2, 110, 55, 80, 25, "", #PB_Text_Border)

  SetProp(#Spin1, "TextGadget", #Text1)
  SetProp(#Spin1, "Multiplier", 5)
  SetGadgetState(#Spin1, 0)

  SetProp(#Spin2, "TextGadget", #Text2)
  SetProp(#Spin2, "Multiplier", 10)
  SetGadgetState(#Spin2, 0)

  Repeat
    Event = WaitWindowEvent()
    If Event = #PB_Event_Gadget
      Gadget = EventGadget()
      SetGadgetText(GetProp(Gadget, "TextGadget"), Str(GetGadgetState(Gadget) * GetProp(Gadget, "Multiplier")))     
    EndIf
  Until Event = #PB_Event_CloseWindow
EndIf