Custom gadget events

Everything else that doesn't fall into one of the other PB categories.
User avatar
tinman
PureBasic Expert
PureBasic Expert
Posts: 1102
Joined: Sat Apr 26, 2003 4:56 pm
Location: Level 5 of Robot Hell
Contact:

Custom gadget events

Post by tinman »

Hi,

I've hacked this up in order to get some custom notification of other events from gadgets (well, one in particular that does not need to keep it's existing behaviour). I've tried to keep it hidden from the end programmer, while maintaining the feel of a normal PB event loop/handler. I'd like to be cross platform, but that could be done with the lower levels being wrapped in compiler directives.

It has some Windows specific parts (inside compiler directives) - can anyone suggest Linux/Mac equivalents? Would it be possible and how much work would it be?

Other suggestions are welcome too, such as alternative methods. I've read Foz's post at http://www.purebasic.fr/english/viewtop ... tom+events and my code is similar, although all his custom events go through the list whereas mine are taken through the normal windows queue (which I'd prefer for this case - gadget events would remain in the order they happened, Foz's code allows events to be processed in parallel).

Thanks.

Code: Select all

EnableExplicit

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Import ""
  PB_Gadget_SendGadgetCommand.l(Window.l, EventType.l)
EndImport
CompilerEndIf

Define.l event
Define.l quit : quit = 0

Define img_id.l

Enumeration
  #WindowMain
EndEnumeration

Enumeration
  #ImageMain
EndEnumeration

Enumeration
  #PB_CustomEvent_LeftMouseDown = $69000000
  #PB_CustomEvent_LeftMouseUp
  #PB_CustomEvent_MouseMove
EndEnumeration

Structure CustomEventData
  event_type.l
  mouse_x.l
  mouse_y.l
EndStructure

Global NewList CustomEventsSent.CustomEventData()
Define *custom_event_data.CustomEventData
Define extracted_event.l
Define extracted_event_type.l

CompilerIf #PB_Compiler_OS = #PB_OS_Windows
Structure MyGadgetUserData
  previous_wndproc.l
EndStructure

Procedure UnsubclassGadget(hwnd.l)
  Protected *user_data.MyGadgetUserData
  
  *user_data = GetWindowLong_(hwnd, #GWL_USERDATA)
  SetWindowLong_(hwnd, #GWL_WNDPROC, *user_data\previous_wndproc)
  SetWindowLong_(hwnd, #GWL_USERDATA, 0)
  FreeMemory(*user_data)
EndProcedure

Procedure.l MyGadgetProc(hwnd.l, message.l, wparam.l, lparam.l)
  Protected result.l
  Protected *user_data.MyGadgetUserData
  Protected old_wndproc.l
  
  *user_data = GetWindowLong_(hwnd, #GWL_USERDATA)
  
  If *user_data <> #Null
    Select message 
      Case #WM_NCDESTROY
        UnsubclassGadget(hwnd)
        
      Case #WM_LBUTTONDOWN
        AddElement(CustomEventsSent())
        CustomEventsSent()\event_type = #PB_CustomEvent_LeftMouseDown
        PB_Gadget_SendGadgetCommand(hwnd, @CustomEventsSent())
        
      Case #WM_LBUTTONUP
        AddElement(CustomEventsSent())
        CustomEventsSent()\event_type = #PB_CustomEvent_LeftMouseUp
        PB_Gadget_SendGadgetCommand(hwnd, @CustomEventsSent())
      
      Case #WM_MOUSEMOVE
        AddElement(CustomEventsSent())
        CustomEventsSent()\event_type = #PB_CustomEvent_MouseMove
        CustomEventsSent()\mouse_x = PeekW(@lparam);
        CustomEventsSent()\mouse_y = PeekW(@lparam+2);
        PB_Gadget_SendGadgetCommand(hwnd, @CustomEventsSent())
      
      Default
        ; You could move this after the Select...EndSelect in order to maintain
        ; existing behaviour but also get notification of the other events
        result = CallWindowProc_(*user_data\previous_wndproc, hwnd, message, wparam, lparam)
    EndSelect
    
  Else
    Debug "Calling default window procedure"
    result = DefWindowProc_(hwnd, message, wparam, lparam)
  EndIf
  
  ProcedureReturn result
EndProcedure

Procedure SubclassGadget(GadgetNumber.l)
  Protected *user_data.MyGadgetUserData
  *user_data = AllocateMemory(SizeOf(MyGadgetUserData))
  *user_data\previous_wndproc = GetWindowLong_(GadgetID(GadgetNumber), #GWL_WNDPROC)
  SetWindowLong_(GadgetID(GadgetNumber), #GWL_USERDATA, *user_data)
  SetWindowLong_(GadgetID(GadgetNumber), #GWL_WNDPROC, @MyGadgetProc())
EndProcedure
CompilerElse
Procedure SubclassGadget(GadgetNumber.l)
EndProcedure
CompilerEndIf

CreateImage(#ImageMain, 200, 200)
If StartDrawing(ImageOutput(#ImageMain))
  Box(0, 0, 200, 200, RGB(Random(256), Random(256), Random(256)))
  StopDrawing()
EndIf

Procedure.l IsCustomEventType(event_type.l)
  Protected is_custom.l = #False
  
  ResetList(CustomEventsSent())
    
  While NextElement(CustomEventsSent()) And is_custom = #False
    If event_type = @CustomEventsSent()
      is_custom = #True
    EndIf
  Wend
  
  ProcedureReturn is_custom
EndProcedure

Procedure ExtractEventData()
  Shared *custom_event_data.CustomEventData
  Shared extracted_event.l
  Shared extracted_event_type.l
  
  If extracted_event = #False
    extracted_event_type = EventType()
    If IsCustomEventType(extracted_event_type)
      *custom_event_data = extracted_event_type
      extracted_event_type = *custom_event_data\event_type
      Debug "Custom event type = "+RSet(Hex(*custom_event_data\event_type), 8, "0")
    Else
      *custom_event_data = #Null
    EndIf
    
    extracted_event = #True
  EndIf
EndProcedure

Procedure.l CustomEventType()
  Shared extracted_event_type.l
  
  ExtractEventData()
    
  ProcedureReturn extracted_event_type
EndProcedure

Procedure.l CustomEventMouseX()
  Shared *custom_event_data
  Protected mouse_x.l
  
  ExtractEventData()
  If *custom_event_data <> #Null
    mouse_x = *custom_event_data\mouse_x
  EndIf
  
  ProcedureReturn mouse_x
EndProcedure

Procedure.l CustomEventMouseY()
  Shared *custom_event_data
  Protected mouse_y.l
  
  ExtractEventData()
  If *custom_event_data <> #Null
    mouse_y = *custom_event_data\mouse_y
  EndIf
  
  ProcedureReturn mouse_y
EndProcedure

Procedure CustomEventHandlerComplete()
  Shared *custom_event_data.CustomEventData
  Shared extracted_event.l
  Shared extracted_event_type.l

  ExtractEventData()
  
  If *custom_event_data<>#Null
    ChangeCurrentElement(CustomEventsSent(), *custom_event_data)
    DeleteElement(CustomEventsSent())
  EndIf
  
  *custom_event_data = #Null
  extracted_event = #False
  extracted_event_type = 0
EndProcedure

Procedure HandleImageMain()
  Select CustomEventType()
    Case #PB_EventType_LeftClick
      Debug "Left mouse clicked"
    Case #PB_CustomEvent_LeftMouseDown
      Debug "Left mouse down"
    Case #PB_CustomEvent_LeftMouseUp
      Debug "Left mouse up"
    Case #PB_CustomEvent_MouseMove
      Debug "Mouse move to "+Str(CustomEventMouseY())+", "+Str(CustomEventMouseX())
  EndSelect
  
  CustomEventHandlerComplete()
EndProcedure

If OpenWindow(#WindowMain, 0, 0, 800, 600, "foo", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget|#PB_Window_TitleBar|#PB_Window_ScreenCentered)

  If CreateGadgetList(WindowID(#WindowMain))
    img_id = ImageGadget(#PB_Any, 10, 10, 200, 200, ImageID(#ImageMain))
  EndIf
  
  SubclassGadget(img_id)

  While Not(quit)
    event = WaitWindowEvent()
    Select event
      Case #PB_Event_CloseWindow
        quit = 1

      Case #PB_Event_Gadget
        Debug "Gadget event"
        If EventGadget()=img_id : HandleImageMain()
        EndIf
          
    EndSelect
  Wend
  
  CloseWindow(#WindowMain)
EndIf
End
If you paint your butt blue and glue the hole shut you just themed your ass but lost the functionality.
(WinXPhSP3 PB5.20b14)
Coss
New User
New User
Posts: 1
Joined: Mon Oct 20, 2008 2:10 pm
Location: Russia, Rostov-on-Don

Post by Coss »

Wow!!!! :D
Post Reply