SetGadgetCallback (Windows Only)

Share your advanced PureBasic knowledge/code with the community.
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

SetGadgetCallback (Windows Only)

Post by mk-soft »

Here is a small module to redirect the window procedures from the gadget controls.

Would be nice if Purebasic supported this directly.

Update v0.03
- Remove NewMap and Mutex
- Change to gadget properties

Update v0.04
- Added option to set the callback to the parent control. A callback that has already been set is replaced!

Update v0.05.1
- Fix: Remove property at destroy

Important notice.
The parent option of SetGadgetCallback replaces the previously directly set gadgdet callback from the parent gadget or parent window.
For example, from a directly set container gadget callback from a set client gadget callback with parent option.

Code: Select all

;-TOP

; Comment : Module SetGadgetCallback (Windows Only)
; Author  : mk-soft
; Version : v0.05.2
; Created : 10.06.2018
; Updated : 26.05.2023
; Link    : https://www.purebasic.fr/english/viewtopic.php?f=12&t=70842
;
; Description
; - A callback that has already been set is replaced by the new callback!
;
; - Syntax Callback:
;           Procedure GadgetCB(hWnd,uMsg,wParam,lParam)
;             Select uMsg
;               ;TODO
;             EndSelect
;             ; Call previous gadget procedure
;             ProcedureReturn CallGadgetProc(hWnd,uMsg,wParam,lParam)
;           EndProcedure
;
; *****************************************************************************

DeclareModule GadgetCallback
  
  Declare SetGadgetCallback(Gadget, *lpNewFunc, Parent = #False) 
  Declare CallGadgetProc(hWnd, uMsg, wParam, lParam)
  
EndDeclareModule

Module GadgetCallback
  
  EnableExplicit
  
  ; ---------------------------------------------------------------------------
  
  Procedure SetGadgetCallback(Gadget, *lpNewFunc, Parent = #False)
    Protected hWnd, *lpPrevFunc
    
    hWnd = GadgetID(Gadget)
    If Parent
      hwnd = GetParent_(hwnd)
    EndIf
    *lpPrevFunc = GetProp_(hWnd, "PB_PrevFunc")
    ; Remove exists Callback
    If *lpPrevFunc
      SetWindowLongPtr_(hWnd, #GWL_WNDPROC, *lpPrevFunc)
      RemoveProp_(hWnd, "PB_PrevFunc")
    EndIf
    ; Set new Callback  
    If *lpNewFunc
      *lpPrevFunc = SetWindowLongPtr_(hWnd, #GWL_WNDPROC, *lpNewFunc)
      SetProp_(hWnd, "PB_PrevFunc", *lpPrevFunc)
      ProcedureReturn *lpPrevFunc
    EndIf
    ProcedureReturn 0
  EndProcedure
  
  ; ---------------------------------------------------------------------------
  
  Procedure CallGadgetProc(hWnd, uMsg, wParam, lParam)
    Protected result, *lpPrevFunc
    
    If uMsg = #WM_NCDESTROY
      *lpPrevFunc = RemoveProp_(hWnd, "PB_PrevFunc")
    Else
      *lpPrevFunc = GetProp_(hWnd, "PB_PrevFunc")
    EndIf
    If *lpPrevFunc
      result = CallWindowProc_(*lpPrevFunc, hWnd, uMsg, wParam, lParam)
    EndIf
    ProcedureReturn result
  EndProcedure
EndModule

; *****************************************************************************

CompilerIf #PB_Compiler_IsMainFile
  
  ;- Example
  
  EnableExplicit
  
  UseModule GadgetCallback
  
  Define hBrushG = CreateSolidBrush_(RGB(0, 255, 0))
  Define hBrushR = CreateSolidBrush_(RGB(255, 0, 0))
  Define hBrushB = CreateSolidBrush_(RGB(0, 0, 255))
  
  ; ----
  
  Procedure GadgetColorCallback(hWnd, uMsg, wParam, lParam)
    Shared hBrushG, hBrushR, hBrushB
    Select uMsg
      Case #WM_CTLCOLORBTN
        ;Debug GetDlgCtrlID_(lParam)
        Select lParam
          Case GadgetID(1)
            ProcedureReturn hBrushG
          Case GadgetID(2)
            ProcedureReturn hBrushR
          Case GadgetID(4)
            ProcedureReturn hBrushB
          Case GadgetID(5)
            ProcedureReturn hBrushR
        EndSelect
    EndSelect
    ProcedureReturn CallGadgetProc(hWnd,uMsg,wParam,lParam)
  EndProcedure
  
  ; ----
  
  Procedure GadgetMouseCallback(hWnd, uMsg, wParam, lParam)
    Protected Gadget
    Select uMsg
      Case #WM_RBUTTONUP
        Gadget = GetProp_(hwnd, "PB_ID")
        PostEvent(#PB_Event_Gadget, GetActiveWindow(), Gadget, #PB_EventType_RightClick)
    EndSelect
    ProcedureReturn CallGadgetProc(hWnd,uMsg,wParam,lParam)
  EndProcedure
  
  ; ----
  
  Procedure Main()
    If OpenWindow(0, 10, 10, 320, 200, "Window", #PB_Window_SystemMenu)
      PanelGadget(0, 0, 0, 320, 100)
        AddGadgetItem (0, -1, "Panel 1")
        ButtonGadget(1, 5, 10, 100, 20, "Button 1")
        AddGadgetItem (0, -1, "Panel 2")
        ButtonGadget(2, 5, 10, 100, 20, "Button 2")
      CloseGadgetList()
      ContainerGadget(3, 5, 110, 310, 40, #PB_Container_Single)  
        ButtonGadget(4, 5, 10, 100, 20, "Button 4")
      CloseGadgetList()
      ButtonGadget(5, 10, 160, 100, 20, "Button 5")
        
      ; MSDN: Some message is sent to the parent window
      SetGadgetCallback(1, @GadgetColorCallback(), #True) ; Parent is Item 1 from PanelGadget
      SetGadgetCallback(2, @GadgetColorCallback(), #True) ; Parent is Item 2 from PanelGadget
      
      SetGadgetCallback(3, @GadgetColorCallback()) ; Contrainer
      ;SetGadgetCallback(4, @GadgetColorCallback(), #True) ; Parent is Contrainer
      SetGadgetCallback(5, @GadgetColorCallback(), #True) ; Parent is Window
      
      SetGadgetCallback(1, @GadgetMouseCallback())
      SetGadgetCallback(2, @GadgetMouseCallback())
      SetGadgetCallback(4, @GadgetMouseCallback())
      SetGadgetCallback(5, @GadgetMouseCallback())
      
      Repeat
        Select WaitWindowEvent()
          Case #PB_Event_CloseWindow
            Break
          Case #PB_Event_Gadget
            Select EventGadget()
              Case 1
                Select EventType()
                  Case #PB_EventType_LeftClick
                    Debug "Left Click Button 1"
                  Case #PB_EventType_RightClick
                    Debug "Right Click Button 1"
                EndSelect
              Case 2
                Select EventType()
                  Case #PB_EventType_LeftClick
                    Debug "Left Click Button 2"
                  Case #PB_EventType_RightClick
                    Debug "Right Click Button 2"
                EndSelect
              Case 4
                Select EventType()
                  Case #PB_EventType_LeftClick
                    Debug "Left Click Button 4"
                  Case #PB_EventType_RightClick
                    Debug "Right Click Button 4"
                EndSelect
              Case 5
                Select EventType()
                  Case #PB_EventType_LeftClick
                    Debug "Left Click Button 5"
                  Case #PB_EventType_RightClick
                    Debug "Right Click Button 5"
                EndSelect
                
            EndSelect
            
        EndSelect
      ForEver
    EndIf
    
  EndProcedure : Main()
  
  DeleteObject_(hBrushG)
  DeleteObject_(hBrushR)
  DeleteObject_(hBrushB)
  
CompilerEndIf
Last edited by mk-soft on Sun May 28, 2023 1:18 pm, edited 17 times in total.
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: SetGadgetCallback (Windows Only)

Post by Kwai chang caine »

Very nice, thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
RSBasic
Moderator
Moderator
Posts: 1218
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: SetGadgetCallback (Windows Only)

Post by RSBasic »

Yes, thanks for sharing. :)
Image
Image
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: SetGadgetCallback (Windows Only)

Post by mk-soft »

Update v0.03
- Remove NewMap and Mutex
- Change to gadget properties

:wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: SetGadgetCallback (Windows Only)

Post by mk-soft »

Update v0.04
- Added option to set the callback to the parent control. A callback that has already been set is replaced!
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: SetGadgetCallback (Windows Only)

Post by mk-soft »

Update v0.04.1
- Update example :wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Axolotl
Enthusiast
Enthusiast
Posts: 449
Joined: Wed Dec 31, 2008 3:36 pm

Re: SetGadgetCallback (Windows Only)

Post by Axolotl »

hi mk-soft,

nice code, thanks for sharing.

Based on this remark in msdn,
Remarks
Before a window is destroyed (that is, before it returns from processing the WM_NCDESTROY message), an application must remove all entries it has added to the property list. The application must use the RemoveProp function to remove the entries.
I would suggest the following change:

Code: Select all

  Procedure CallGadgetProc(hWnd, uMsg, wParam, lParam) 
    Protected result, *lpPrevFunc
    
    If uMsg = #WM_NCDESTROY 
      *lpPrevFunc = RemoveProp_(hWnd, "PB_PrevFunc")  
    Else
      *lpPrevFunc = GetProp_(hWnd, "PB_PrevFunc") 
    EndIf 

    If *lpPrevFunc
      result = CallWindowProc_(*lpPrevFunc, hWnd, uMsg, wParam, lParam)
    EndIf 

    ProcedureReturn result
  EndProcedure
  
Mostly running PureBasic <latest stable version and current alpha/beta> (x64) on Windows 11 Home
User avatar
mk-soft
Always Here
Always Here
Posts: 5405
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: SetGadgetCallback (Windows Only)

Post by mk-soft »

Update v0.05.1
- Fix: Remove property at destroy

Thank Axolotl :wink:
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
Post Reply