Seite 1 von 4

EM_EventManager (crossplattform)

Verfasst: 03.03.2013 17:47
von ts-soft
Hier meine Version eines EventManagers :wink:

Code: Alles auswählen

;======================================================================
; Library:         EM_EventManager.pbi
;
; Author:          Thomas (ts-soft) Schulz
; Date:            March 30, 2013
; Version:         1.5
; Target Compiler: PureBasic 5.1+
; Target OS:       Windows, Linux, MacOS
; License:         Free, unrestricted, no warranty whatsoever
;                  Use at your own risk
;======================================================================
; Description:
;   All parts of the normal event loop can be used without restriction or
;   replaced by eventhandlers. Integration into existing projects should no problem
;
;   EM_SetEventHandler(ID, ObjectType, *function, EventType = -1)
;     ID = ID of Object (Window, Gadget, MenuItem, Timer, SysTray icon or EventNumber like #PB_Event_CloseWindow, #PB_Event_SizeWindow, #PB_Event_FirstCustomValue ...
;     ObjectType = see ObjectType Enumeration
;     *function = Address of Procedure to handle event, should only have one parameter: Procedure Foo(*ev.EM_Events)
;     EventType = the #PB_EventType for Type #EM_Window, #EM_Gadget, and #EM_SysTray
;
; EM_RemoveEventHandler(ID, ObjectType, EventType = -1)
;     ID = ID of Object (Window, Gadget, MenuItem, Timer, SysTray icon or EventNumber like #PB_Event_CloseWindow, #PB_Event_SizeWindow, #PB_Event_FirstCustomValue ...
;     EventType = the #PB_EventType for Type #EM_Window, #EM_Gadget, and #EM_SysTray
;
; History:
; Version 1.5 (March 30, 2013) - by Danilo
; added #EM_Window for window events
; added #PB_Event_GadgetDrop to gadget event system
;
; Version 1.4 (March 30, 2013) - by Danilo
; added #EM_SysTray for SysTrayIcon events
;
; Version 1.3 (March 05, 2013)
; fixed a memoryleak with maps, comes with Version 1.2
;
; Version 1.2 (March 04, 2013)
; Codeoptimization (removed map events(), some FindMapElement ...
;
; Version 1.1
; changed from CallFunctionFast to Prototype, thx to netmaestro!
;
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf

Enumeration ; ObjectType
  #EM_Window
  #EM_Gadget
  #EM_Menu
  #EM_SysTray
  #EM_Timer
  #EM_Special
EndEnumeration

Structure EM_Events
  Event.i
  EventType.i
  EventWindow.i
  EventGadget.i
  EventMenu.i
  EventSysTray.i
  EventTimer.i
  EventData.i
  CompilerIf #PB_Compiler_OS = #PB_OS_Windows
    EventwParam.i
    EventlParam.i
  CompilerEndIf
EndStructure

Structure __EM_Maps__
  Map WindowEvents.i()
  Map GadgetEvents.i()
  Map MenuEvents.i()
  Map SysTrayEvents.i()
  Map TimerEvents.i()
  Map SpecialEvents.i()
EndStructure

Prototype __EM_ProcessEvent__(*Event.EM_Events)

Global __EM_Map__.__EM_Maps__

Procedure EM_SetEventHandler(ID, ObjectType, *function.__EM_ProcessEvent__, EventType = -1)
  If *function <> 0
    Select ObjectType
      Case #EM_Window
        If IsWindow(ID)
          __EM_Map__\WindowEvents(Str(ID) + ":" + Str(EventType)) = *function
        EndIf
        
      Case #EM_Gadget
        If IsGadget(ID)
          __EM_Map__\GadgetEvents(Str(ID) + ":" + Str(EventType)) = *function
        EndIf
        
      Case #EM_SysTray
        If IsSysTrayIcon(ID)
          __EM_Map__\SysTrayEvents(Str(ID) + ":" + Str(EventType)) = *function
        EndIf
        
      Case #EM_Menu
        __EM_Map__\MenuEvents(Str(ID)) = *function
        
      Case #EM_Timer
        __EM_Map__\TimerEvents(Str(ID)) = *function
        
      Case #EM_Special
        __EM_Map__\SpecialEvents(Str(ID)) = *function
        
    EndSelect
  EndIf
EndProcedure

Procedure EM_RemoveEventHandler(ID, ObjectType, EventType = -1)
  Select ObjectType
    Case #EM_Window
      DeleteMapElement(__EM_Map__\WindowEvents(), Str(ID) + ":" + Str(EventType))
      
    Case #EM_Gadget
      DeleteMapElement(__EM_Map__\GadgetEvents(), Str(ID) + ":" + Str(EventType))
      
    Case #EM_SysTray
      DeleteMapElement(__EM_Map__\SysTrayEvents(), Str(ID) + ":" + Str(EventType))
      
    Case #EM_Menu
      DeleteMapElement(__EM_Map__\MenuEvents(), Str(ID))
      
    Case #EM_Timer
      DeleteMapElement(__EM_Map__\TimerEvents(), Str(ID))
      
    Case #EM_Special
      DeleteMapElement(__EM_Map__\SpecialEvents(), Str(ID))
  EndSelect
EndProcedure

Procedure __EM_WaitWindowEvent__(timeout.i = -1)
  Protected Event.EM_Events, ProcessThisEvent.__EM_ProcessEvent__
  
  With Event 
    \Event = WaitWindowEvent(timeout)
    \EventType    = EventType()
    \EventWindow  = EventWindow()
    \EventGadget  = EventGadget()
    \EventMenu    = EventMenu()
    \EventTimer   = EventTimer()
    \EventData    = EventData()
    CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      \EventwParam  = EventwParam()
      \EventlParam  = EventlParam()
    CompilerEndIf
    
    Select \Event
        
      Case #PB_Event_Gadget, #PB_Event_GadgetDrop
        If \Event = #PB_Event_GadgetDrop
          \EventType = \Event
        EndIf
        If FindMapElement(__EM_Map__\GadgetEvents(), Str(\EventGadget) + ":" + Str(\EventType))
          If __EM_Map__\GadgetEvents()
            ProcessThisEvent = __EM_Map__\GadgetEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        ElseIf FindMapElement(__EM_Map__\GadgetEvents(), Str(\EventGadget) + ":-1")
          If __EM_Map__\GadgetEvents()
            ProcessThisEvent = __EM_Map__\GadgetEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
          ; for compatibility with special events:
        ElseIf \Event = #PB_Event_GadgetDrop And FindMapElement(__EM_Map__\SpecialEvents(), Str(\Event))
          If __EM_Map__\SpecialEvents()
            ProcessThisEvent = __EM_Map__\SpecialEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        EndIf
      Case #PB_Event_SysTray
        \EventSysTray = \EventGadget
        If FindMapElement(__EM_Map__\SysTrayEvents(), Str(\EventSysTray) + ":" + Str(\EventType))
          If __EM_Map__\SysTrayEvents()
            ProcessThisEvent = __EM_Map__\SysTrayEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        ElseIf FindMapElement(__EM_Map__\SysTrayEvents(), Str(\EventSysTray) + ":-1")
          If __EM_Map__\GadgetEvents()
            ProcessThisEvent = __EM_Map__\SysTrayEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf       
        EndIf
      Case #PB_Event_Menu
        If FindMapElement(__EM_Map__\MenuEvents(), Str(\EventMenu))
          If __EM_Map__\MenuEvents()
            ProcessThisEvent = __EM_Map__\MenuEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        EndIf
      Case #PB_Event_Timer
        If FindMapElement(__EM_Map__\TimerEvents(), Str(\EventTimer))
          If __EM_Map__\TimerEvents()
            ProcessThisEvent = __EM_Map__\TimerEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        EndIf
      Case #PB_Event_CloseWindow,
           #PB_Event_Repaint,
           #PB_Event_SizeWindow,
           #PB_Event_MoveWindow,
           #PB_Event_MinimizeWindow,
           #PB_Event_MaximizeWindow,
           #PB_Event_RestoreWindow,
           #PB_Event_ActivateWindow,
           #PB_Event_DeactivateWindow,
           #PB_Event_WindowDrop,
           #PB_Event_RightClick,
           #PB_Event_LeftClick,
           #PB_Event_LeftDoubleClick
        \EventType = \Event
        If FindMapElement(__EM_Map__\WindowEvents(), Str(\EventWindow) + ":" + Str(\Event))
          If __EM_Map__\WindowEvents()
            ProcessThisEvent = __EM_Map__\WindowEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        ElseIf FindMapElement(__EM_Map__\WindowEvents(), Str(\EventWindow) + ":-1")
          If __EM_Map__\WindowEvents()
            ProcessThisEvent = __EM_Map__\WindowEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
          ; for compatibility with special events:
        ElseIf FindMapElement(__EM_Map__\SpecialEvents(), Str(\Event))
          If __EM_Map__\SpecialEvents()
            ProcessThisEvent = __EM_Map__\SpecialEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        EndIf
        
      Default
        If FindMapElement(__EM_Map__\SpecialEvents(), Str(\Event))
          If __EM_Map__\SpecialEvents()
            ProcessThisEvent = __EM_Map__\SpecialEvents()
            If ProcessThisEvent
              ProcessThisEvent(Event)
            EndIf
          EndIf
        EndIf
    EndSelect
    
    ProcedureReturn Event\Event
  EndWith
EndProcedure

Macro WaitWindowEvent(timeout = -1)
  __EM_WaitWindowEvent__(timeout)
EndMacro

Macro WindowEvent()
  __EM_WaitWindowEvent__(0)
EndMacro

CompilerIf #PB_Compiler_IsMainFile
  ; small example
  Procedure Event_CloseWindow(*ev.EM_Events)
    End
  EndProcedure
  
  Procedure Event_btnOkay_Clicked(*ev.EM_Events)
    Debug "Okay clicked"
  EndProcedure
  
  Procedure Event_btnCancel_Clicked(*ev.EM_Events)
    Debug "Cancel clicked"
  EndProcedure
  
  Procedure Event_btnQuit_Clicked(*ev.EM_Events)
    PostEvent(#PB_Event_CloseWindow, *ev\EventWindow, -1)
    ;Event_CloseWindow(*ev)
    ;End
  EndProcedure
  
  Procedure Event_SysTray_Clicked(*ev.EM_Events)
    MessageRequester("INFO","SysTray Icon clicked: "+Str(*ev\EventSysTray))
  EndProcedure
  
  Procedure Event_SysTray1_RightClick(*ev.EM_Events)
    Protected menu = CreatePopupMenu(#PB_Any)
    If menu
      MenuItem(1, "Item 1")
      MenuItem(2, "Item 2")
      MenuItem(3, "Item 3")
      MenuItem(4, "Item 4")
      
      DisplayPopupMenu(menu,WindowID(*ev\EventWindow))
    EndIf
  EndProcedure
  
  
  OpenWindow(0, #PB_Ignore, #PB_Ignore, 340, 45, "EM_EventManager - Example")
  EM_SetEventHandler(0, #EM_Window, @Event_CloseWindow(), #PB_Event_CloseWindow)
  ;EM_SetEventHandler(#PB_Event_CloseWindow, #EM_Special, @Event_CloseWindow())
  
  ButtonGadget(0, 10, 10, 100, 25, "Okay")
  EM_SetEventHandler(0, #EM_Gadget, @Event_btnOkay_Clicked(), #PB_EventType_LeftClick)
  
  Define btnCancel = ButtonGadget(#PB_Any, 120, 10, 100, 25, "Cancel")
  EM_SetEventHandler(btnCancel, #EM_Gadget, @Event_btnCancel_Clicked(), #PB_EventType_LeftClick)
  
  ButtonGadget(1, 230, 10, 100, 25, "Quit")
  EM_SetEventHandler(1, #EM_Gadget, @Event_btnQuit_Clicked(), #PB_EventType_LeftClick)
  
  If CreateImage(1,16,16)
    
    AddSysTrayIcon(1, WindowID(0), ImageID(1))
    EM_SetEventHandler(1, #EM_SysTray, @Event_SysTray_Clicked(), #PB_EventType_LeftClick)
    EM_SetEventHandler(1, #EM_SysTray, @Event_SysTray1_RightClick(), #PB_EventType_RightClick)
    
    AddSysTrayIcon(2, WindowID(0), ImageID(1))
    EM_SetEventHandler(2, #EM_SysTray, @Event_SysTray_Clicked(), #PB_EventType_LeftClick)
  EndIf
  
  Repeat
    WaitWindowEvent()
  ForEver
CompilerEndIf
Solltet Ihr noch Fehler finden, so meldet die bitte hier.

Gruß
Thomas

GELÖSCHT

Verfasst: 03.03.2013 17:57
von mirca
GELÖSCHT

Re: EM_EventManager (crossplattform)

Verfasst: 03.03.2013 18:04
von ts-soft
Das ist eine Vereinfachung (sobald man den Umgang damit kennt) und es macht den Code übersichtlicher.

Es ist sozusagen: EventDriven :wink: , ähnlich wie man es von anderen Sprachen kennt.
Besonders nützlich wird es, wenn Du eigene Gadgets z.B. auf Basis des CanvasGadget entwirfst
und die notwendige Ereignisverhandlung für Dein Gadget "unauffällig" bearbeiten möchtest.

Mehr fällt mir gerade nicht ein, aber ich kann es gebrauchen :wink:

Re: EM_EventManager (crossplattform)

Verfasst: 03.03.2013 19:06
von ts-soft
Kleines Update:
Historie hat geschrieben:; Version 1.1
; changed from CallFunctionFast to Prototype, thx to netmaestro!

Re: EM_EventManager (crossplattform)

Verfasst: 03.03.2013 19:23
von Kiffi
Schick! Danke! :allright:

Grüße ... Kiffi

Re: EM_EventManager (crossplattform)

Verfasst: 04.03.2013 19:43
von ts-soft
Hier noch ein etwas dickeres Beispiel :mrgreen:
Ist ein Beispiel aus der Hilfe, angepaßt:

Code: Alles auswählen

;
; ------------------------------------------------------------
;
;   PureBasic - Drag & Drop
;
;    (c) 2007 - Fantaisie Software
;
; ------------------------------------------------------------
;
; changed to work with EM_EventManger.pbi by ts-soft
EnableExplicit

XIncludeFile "EM_EventManager.pbi"

#Window = 0

Enumeration    ; Images
  #ImageSource
  #ImageTarget
EndEnumeration

Enumeration    ; Gadgets
  #SourceText
  #SourceImage
  #SourceFiles
  #SourcePrivate
  #TargetText
  #TargetImage
  #TargetFiles
  #TargetPrivate1
  #TargetPrivate2
EndEnumeration

Procedure SourceText_DragStart(*ev.EM_Events)
  Protected Text$ = GetGadgetItemText(#SourceText, GetGadgetState(#SourceText))
  DragText(Text$)
EndProcedure

Procedure SourceImage_DragStart(*ev.EM_Events)
  DragImage(ImageID(#ImageSource))
EndProcedure

Procedure SourceFiles_DragStart(*ev.EM_Events)
  Protected i
  Protected Files$ = ""       
  For i = 0 To CountGadgetItems(#SourceFiles)-1
    If GetGadgetItemState(#SourceFiles, i) & #PB_Explorer_Selected
      Files$ + GetGadgetText(#SourceFiles) + GetGadgetItemText(#SourceFiles, i) + Chr(10)
    EndIf
  Next i 
  If Files$ <> ""
    DragFiles(Files$)
  EndIf
EndProcedure

Procedure SourcePrivate_DragStart(*ev.EM_Events)
  If GetGadgetState(#SourcePrivate) = 0
    DragPrivate(1)
  Else
    DragPrivate(2)
  EndIf
EndProcedure

Procedure Event_GadgetDrop(*ev.EM_Events)
  Protected Files$, i, Count
  
  Select *ev\EventGadget
  
    Case #TargetText
      AddGadgetItem(#TargetText, -1, EventDropText())
    
    Case #TargetImage
      If EventDropImage(#ImageTarget)
        SetGadgetState(#TargetImage, ImageID(#ImageTarget))
      EndIf
    
    Case #TargetFiles
      Files$ = EventDropFiles()
      Count  = CountString(Files$, Chr(10)) + 1
      For i = 1 To Count
        AddGadgetItem(#TargetFiles, -1, StringField(Files$, i, Chr(10)))
      Next i
    
    Case #TargetPrivate1
      AddGadgetItem(#TargetPrivate1, -1, "Private type 1 dropped")
            
    Case #TargetPrivate2
      AddGadgetItem(#TargetPrivate2, -1, "Private type 2 dropped")
    
  EndSelect
EndProcedure

Procedure Window_Close(*ev.EM_Events)
  End
EndProcedure

Macro DoEvent()
  Repeat : WaitWindowEvent() : ForEver
EndMacro

Define i

If OpenWindow(#Window, 0, 0, 760, 310, "Drag & Drop", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  EM_SetEventHandler(#PB_Event_CloseWindow, #EM_Special, @Window_Close())
  ; Create some images for the image demonstration
  ; 
  CreateImage(#ImageSource, 136, 136)
  If StartDrawing(ImageOutput(#ImageSource))
    Box(0, 0, 136, 136, $FFFFFF)
    DrawText(5, 5, "Drag this image", $000000, $FFFFFF)        
    For i = 45 To 1 Step -1
      Circle(70, 80, i, Random($FFFFFF))
    Next i        
    
    StopDrawing()
  EndIf  
  
  CreateImage(#ImageTarget, 136, 136)
  If StartDrawing(ImageOutput(#ImageTarget))
    Box(0, 0, 136, 136, $FFFFFF)
    DrawText(5, 5, "Drop images here", $000000, $FFFFFF)
    StopDrawing()
  EndIf  
  
  
  ; Create and fill the source gadgets
  ;
  ListIconGadget(#SourceText,       10, 10, 140, 140, "Drag Text here", 130)
  EM_SetEventHandler(#SourceText, #EM_Gadget, @SourceText_DragStart(), #PB_EventType_DragStart)
  ImageGadget(#SourceImage,        160, 10, 140, 140, ImageID(#ImageSource), #PB_Image_Border)
  EM_SetEventHandler(#SourceImage, #EM_Gadget, @SourceImage_DragStart(), #PB_EventType_DragStart)
  ExplorerListGadget(#SourceFiles, 310, 10, 290, 140, GetHomeDirectory(), #PB_Explorer_MultiSelect)
  EM_SetEventHandler(#SourceFiles, #EM_Gadget, @SourceFiles_DragStart(), #PB_EventType_DragStart)
  ListIconGadget(#SourcePrivate,   610, 10, 140, 140, "Drag private stuff here", 260)
  EM_SetEventHandler(#SourcePrivate, #EM_Gadget, @SourcePrivate_DragStart(), #PB_EventType_DragStart)
     
  AddGadgetItem(#SourceText, -1, "hello world")
  AddGadgetItem(#SourceText, -1, "The quick brown fox jumped over the lazy dog")
  AddGadgetItem(#SourceText, -1, "abcdefg")
  AddGadgetItem(#SourceText, -1, "123456789")
  
  AddGadgetItem(#SourcePrivate, -1, "Private type 1")
  AddGadgetItem(#SourcePrivate, -1, "Private type 2")
  

  ; Create the target gadgets
  ;
  ListIconGadget(#TargetText,  10, 160, 140, 140, "Drop Text here", 130)
  ImageGadget(#TargetImage,    160, 160, 140, 140, ImageID(#ImageTarget), #PB_Image_Border) 
  ListIconGadget(#TargetFiles, 310, 160, 140, 140, "Drop Files here", 130)
  ListIconGadget(#TargetPrivate1, 460, 160, 140, 140, "Drop Private Type 1 here", 130)
  ListIconGadget(#TargetPrivate2, 610, 160, 140, 140, "Drop Private Type 2 here", 130)

  
  ; Now enable the dropping on the target gadgets
  ;
  EnableGadgetDrop(#TargetText,     #PB_Drop_Text,    #PB_Drag_Copy)
  EnableGadgetDrop(#TargetImage,    #PB_Drop_Image,   #PB_Drag_Copy)
  EnableGadgetDrop(#TargetFiles,    #PB_Drop_Files,   #PB_Drag_Copy)
  EnableGadgetDrop(#TargetPrivate1, #PB_Drop_Private, #PB_Drag_Copy, 1)
  EnableGadgetDrop(#TargetPrivate2, #PB_Drop_Private, #PB_Drag_Copy, 2)
  
  EM_SetEventHandler(#PB_Event_GadgetDrop, #EM_Special, @Event_GadgetDrop())

EndIf

DoEvent()


Re: EM_EventManager (crossplattform)

Verfasst: 05.03.2013 10:54
von ts-soft
Update:
History hat geschrieben:; Version 1.3 (March 05, 2013)
; fixed a memoryleak with maps, comes with Version 1.2

Re: EM_EventManager (crossplattform)

Verfasst: 05.03.2013 11:54
von Danilo
ts-soft hat geschrieben:Das ist eine Vereinfachung (sobald man den Umgang damit kennt) und es macht den Code übersichtlicher.

Es ist sozusagen: EventDriven :wink: , ähnlich wie man es von anderen Sprachen kennt.
Vielen Dank, das ist eine der besten Ideen der letzten 10 Jahre! :D

Re: EM_EventManager (crossplattform)

Verfasst: 05.03.2013 12:06
von ts-soft
Danilo hat geschrieben:Vielen Dank, das ist eine der besten Ideen der letzten 10 Jahre! :D
So Neu ist die Idee nicht :wink: , siehe:
http://www.purebasic.fr/german/viewtopi ... 97#p273297
http://www.purebasic.fr/english/viewtop ... 22#p210822
http://www.purebasic.fr/english/viewtop ... 20#p406620
...

aber ich hoffe meine Umsetzung hat sich trotzdem gelohnt, weil
kompakt, schnell, einfach, crossplattform und man hat für jedes
Event die freie Wahl, die herkömmliche Methode oder einen Event-
Handler zu nutzen. Also auch Kompatibel zu vorhandenen Code.


Gruß
Thomas

Re: EM_EventManager (crossplattform)

Verfasst: 05.03.2013 12:22
von Danilo
Vielen Dank, das ist eine der besten Umsetzungen der nachrichtengesteuerten Ereignisverarbeitungungen seit langer Zeit! :D