Event Driven Coding

Share your advanced PureBasic knowledge/code with the community.
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Event Driven Coding

Post by fsw »

Code updated For 5.20+

Here it is - a little gift from me (as you will see there is not much to it - really...).
This code shows how to do it with pb.
I'm sure some users out there will make several variants out of it... even with Linked Lists.

Code: Select all

; Event Driven Coding can be so easy.
; PureBasic only code... no external lib necessary.
; (c) 2005 - by Franco (aka fsw)
; done from scratch in a few minutes
;- Start Main

Declare Button_1_Function()

;MaxObjects(0) is used to store the actual used number
;you could do it with linked lists too...
MaxObjects = 10 ; because there is no 'redim preserve' in pb - bummer...
Global Dim ObjectID(MaxObjects)

Procedure GetNewObjectID()
  ObjectID(0) = ObjectID(0) + 1
  ProcedureReturn ObjectID(0)
EndProcedure

Procedure ConnectTheGadgetToFunction(EventID, Function)
  ObjectID(EventID) = Function
EndProcedure

Procedure CallStoredEventFunction(EventID)
  CallFunctionFast(ObjectID(EventID))
EndProcedure

If OpenWindow(1,100,200,320,240,"Test",#PB_Window_SystemMenu) = 0 : End : EndIf



PB_Button_0 = GetNewObjectID()
ButtonGadget(PB_Button_0,100,50,100,50,"Button ")
ConnectTheGadgetToFunction(PB_Button_0, ?Button_0_Function) ; it works with labels

PB_Button_1 = GetNewObjectID()
ButtonGadget(PB_Button_1,100,150,100,50,"Button ")
ConnectTheGadgetToFunction(PB_Button_1, @Button_1_Function()) ; it works with procedures

Repeat
  Event = WaitWindowEvent()
  
  Select Event
    Case #PB_Event_Gadget
      CallStoredEventFunction(EventGadget())
      
  EndSelect
Until Event = #PB_Event_CloseWindow

End

;- User Functions

Button_0_Function:
MessageRequester("Test Button_0", "It works...",0)
Return

Procedure Button_1_Function()
  MessageRequester("Test Button_1", "It works too...",0)
EndProcedure

It's so easy - even with pb :shock:

ps: my first pb code after a few month and it works :lol:
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

New Approach

Post by fsw »

Never saw such an approach, but I got this idea, tested it with
FreeBasic and it works great.

Now here is the PureBasic version of it.

Could somebody from the PureBasic Team (Fred, Freak etc.)
tell me if the #GWL_USERDATA memory space is used internally?
Is it safe to use the #GWL_USERDATA memory space with PureBasic?

I read out the #GWL_USERDATA memory space from a Gadget
and it was always ZERO, so I think as for now it's not used.

Don't hesitate to comment on this.

Code: Select all

;
; Event Driven Coding Example
;
; Copyright 2005 - by fsw, 4th November 2005
;
; This PureBasic code/idea can not be used commercially or as
; donationware without explicit permission from the author.
;
; If you use this code/idea for freeware, it would be nice 
; to be informed.
; 
; In any case please mention it in your about dialog. 
; Give credit where credit is due. Thanks.
;
; Explanation:
; Here some code if you go the event driven route.

; In the past I used the control ID to manage the calling of the gadget function. 
; In addition an array to store the Function address, and a variable to store the max ID's was needed.

; Now I got the idea to use the #GWL_USERDATA memory space (that is inside every windows control)
; to store the address of the function that needs to be called.
; The only thing needed is SetGadgetFunction and GetGadgetFunction, because now the Gadget hwnd 
; is used to get the function address. 

; This way you can add 1000 buttons to your application and the callback doesn't need to be changed.



Procedure SetGadgetFunction(Control.l, FuncAddr.l)
  ProcedureReturn SetWindowLong_(Control, #GWL_USERDATA, FuncAddr)
EndProcedure

Procedure GetGadgetFunction(Control.l)
  ProcedureReturn GetWindowLong_(Control, #GWL_USERDATA)
EndProcedure

Procedure myTest1()
  MessageRequester("Message", "It's me... # 1")
EndProcedure

Procedure myTest2()
  MessageRequester("Message", "It's me... # 2")
EndProcedure
Procedure myTest3()
  MessageRequester("Message", "It's me... # 3")
EndProcedure

If OpenWindow(1, 0, 0, 322, 120, #PB_Window_SystemMenu | #PB_Window_ScreenCentered, "Event Driven Window")
  CreateGadgetList(WindowID())

  ButtonGadget (1,10, 10,100,100,"Button 1")
  SetGadgetFunction(GadgetID(1), @myTest1())

  ButtonGadget (2,110, 10,100,100,"Button 2")
  SetGadgetFunction(GadgetID(2), @myTest2())

  ButtonGadget (3,210, 10,100,100,"Button 3")
  SetGadgetFunction(GadgetID(3), @myTest3())


  Repeat
    
    EventID = WindowEvent()
    
    Select EventID
     
      Case #PB_Event_Gadget
        CallFunctionFast(GetGadgetFunction(GadgetID(EventGadgetID())))
     EndSelect
     
  Until EventID = #PB_Event_CloseWindow
EndIf 
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

In the old days, with a language like Forth, this chaining together of objects and/or code through pointers was called 'threading'. The concept isn't new, but I've never seen it used like this.
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

The NEW thing about this is that the function address is stored inside the Windows (OS) Control itself.

The code overhead of event driven coding, especially for small programs, is now totally negligible, because no extra array/list/variable is needed.

Also the managment of the array/list/variable is totally obsolete.


If fact you save a lot of code.
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Sometimes we use the userdata, to be sure, i suggest to use another field using the Get/GetProp_() API function :). A little suggestion to be more PB like, you can move the GadgetID() inside the procedure (so you pass the number to your registering callback).
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

Fred wrote:Sometimes we use the userdata, to be sure, i suggest to use another field using the Get/GetProp_() API function :).
But then a list is needed, because every PROP entry needs to be cleaned up before closing the app. :cry:
Fred wrote: A little suggestion to be more PB like, you can move the GadgetID() inside the procedure (so you pass the number to your registering callback).
yep :wink:

Sorry, didn't code much in PB lately...
remi_meier
Enthusiast
Enthusiast
Posts: 468
Joined: Sat Dec 20, 2003 6:19 pm
Location: Switzerland

Post by remi_meier »

Just another way :)
http://forums.purebasic.com/german/view ... orientiert

I knew I've forgotten some codes in "Some Codes"...
Athlon64 3700+, 1024MB Ram, Radeon X1600
Straker
Enthusiast
Enthusiast
Posts: 701
Joined: Wed Apr 13, 2005 10:45 pm
Location: Idaho, USA

Post by Straker »

Always like your stuff, fsw. Keep it up.
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

Thanks, but in this case (the second example in this topic) because Fred said: they sometimes use the #GWL_USERDATA memory-space internally, it's not really usable, is it?

BTW: Thinking of it: because the #GWL_USERDATA memory-space is sometimes internally used and sometimes not, it sounds like a patched product :shock:

:? nevermind...
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

you can still subclass the gadget to remove the prop when it got destroyed, i don't see where is the problem. About the userdata usage, i will see what can be done for future versions.
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

Sure I could, but simplicity was the main point.
Simplicity was the reason I posted the code in the first place.
Post Reply