Another little utility module which I've put together following a design pattern that I've used in a lot of projects that allows you to make more modular applications.
Features
- Handle GUI applications and have the application terminate when the main window closes.
- Register event handlers to fire custom events.
- Register interval functions that will be called every x milliseconds, or every cycle.
- Pass user data to the events And interval functions.
Code: Select all
; ====================================================================================================
; Title: Application Module
; Description: Utility module for managing application runtime.
; Author: Michael 'Micah' King (micah@indigofuzz.co.uk)
; Revision: 1 (03 FEB 2016)
; License: Any improvements to be shared with the community.
; Website: http://www.indigofuzz.co.uk
; ====================================================================================================
; - Declare App Module
DeclareModule App
Enumeration Events ; - Standard Events and Base for Custom Events
#Event_Initialise
#Event_Release
EndEnumeration
Prototype.a pEvent(Event, *EventInfo) ; - Prototype for Event Handlers
Prototype.a pCallback(*UserData) ; - Prototype for Interval Callbacks
Declare ConnectEventHandler(Event, *Callback.pEvent) ; - Connect a new Event Handler
Declare DisconnectEventHandler(Event, *Callback.pEvent) ; - Disconnect an Event Handler
Declare BroadcastEvent(Event, *EventInfo = #Null) ; - Broadcast new Event
Declare RegisterInterval(*Callback, Interval = 0, *UserData = #Null) ; - Register an Interval Callback
Declare UnregisterInterval(*Callback) ; - Unregister an Interval Callback
Declare HandleWindowEvents(State = #True) ; - Enable to call WindowEvent() each Loop
Declare Execute(MainForm = #PB_Ignore, QuitOnClose = #False) ; - Execute (You can specify a Main Window)
Declare Quit() ; - Break Main Loop
EndDeclareModule
; - Implement App Module
Module App
Structure EVTHANDLER
eventID.i
*handler.pEvent
EndStructure
Structure LOOPHANDLER
interval.i
lastCall.i
*userData
*handler.pCallback
EndStructure
Structure INSTANCE
List evtHandlers.EVTHANDLER()
List loopHandler.LOOPHANDLER()
handleUI.a
mainForm.i
quitOnClose.a
execute.a
EndStructure
Global this.INSTANCE
this\mainForm = #PB_Ignore
; - Event Handlers
Procedure ConnectEventHandler(Event, *Callback.pEvent)
With this
If *Callback
ForEach \evtHandlers()
If \evtHandlers()\eventID = Event And \evtHandlers()\handler = *Callback
ProcedureReturn #True
EndIf
Next
AddElement(\evtHandlers())
\evtHandlers()\eventID = Event
\evtHandlers()\handler = *Callback
EndIf
ProcedureReturn #False
EndWith
EndProcedure
Procedure DisconnectEventHandler(Event, *Callback.pEvent)
With this
If *Callback
ForEach \evtHandlers()
If \evtHandlers()\eventID = Event And \evtHandlers()\handler = *Callback
DeleteElement(\evtHandlers())
ProcedureReturn #True
EndIf
Next
EndIf
ProcedureReturn #False
EndWith
EndProcedure
Procedure BroadcastEvent(Event, *EventInfo = #Null)
With this
ForEach \evtHandlers()
If \evtHandlers()\eventID = Event
\evtHandlers()\handler(Event, *EventInfo)
EndIf
Next
EndWith
EndProcedure
; - Interval Functions
Procedure RegisterInterval(*Callback, Interval = 0, *UserData = #Null)
With this
If *Callback
ForEach \loopHandler()
If \loopHandler()\handler = *Callback
\loopHandler()\interval = Interval
\loopHandler()\lastCall = *UserData
\loopHandler()\lastCall = ElapsedMilliseconds()
ProcedureReturn #True
EndIf
Next
AddElement(\loopHandler())
\loopHandler()\handler = *Callback
\loopHandler()\userData = *UserData
\loopHandler()\interval = Interval
\loopHandler()\lastCall = ElapsedMilliseconds()
ProcedureReturn #True
EndIf
EndWith
EndProcedure
Procedure UnregisterInterval(*Callback)
With this
If *Callback
ForEach \loopHandler()
If \loopHandler()\handler = *Callback
DeleteElement(\loopHandler())
ProcedureReturn #True
EndIf
Next
EndIf
EndWith
ProcedureReturn #True
EndProcedure
; - Main Loop
Procedure doIntervals()
Protected time.i = ElapsedMilliseconds()
With this
ForEach \loopHandler()
If \loopHandler()\interval > 0
If \loopHandler()\lastCall + \loopHandler()\interval < time
\loopHandler()\handler(\loopHandler()\userData)
\loopHandler()\lastCall = time
EndIf
Else
\loopHandler()\handler(\loopHandler()\userData)
EndIf
Next
EndWith
EndProcedure
Procedure mainLoop()
With this
While \execute = #True
If \handleUI
If \mainForm > -1
If \quitOnClose
If IsWindow(\mainForm) = #False
Break
EndIf
Else
If IsWindow(\mainForm)
WindowEvent()
EndIf
EndIf
Else
WindowEvent()
EndIf
EndIf
doIntervals()
Wend
EndWith
EndProcedure
Procedure HandleWindowEvents(State = #True)
With this
\handleUI = State
EndWith
EndProcedure
Procedure Execute(MainForm = #PB_Ignore, QuitOnClose = #False)
With this
\mainForm = MainForm
\quitOnClose = QuitOnClose
\execute = #True
If \mainForm > -1 And IsWindow(\mainForm)
\handleUI = #True
EndIf
EndWith
BroadcastEvent(#Event_Initialise)
mainLoop()
BroadcastEvent(#Event_Release)
EndProcedure
Procedure Quit()
With this
\execute = #False
EndWith
EndProcedure
EndModule
; - Demonstration of the App Module
; - Execute with Debugger On
;IncludePath "../include"
;XIncludeFile "ifz/app.pbi"
EnableExplicit
; - Something to pass through the Interval Callback
Structure IntervalInfo
Count.i
EndStructure
Global Interval.IntervalInfo
; - A Custom Event (Inherits Standard Events)
Enumeration App::Events
#Event_Custom
EndEnumeration
; - Do this when an Event is Called
Procedure OnEvent(Event, *EventInfo)
Debug "Event Called. Event ID: " + Str(Event)
EndProcedure
; - Call this Interval and do other things
Procedure OnInterval(*UserData)
Protected *info.IntervalInfo = *UserData
Debug "Interval Called. Count: " + *info\Count
*info\Count + 1
Select *info\Count
Case 3
App::BroadcastEvent(#Event_Custom)
Case 5
App::Quit()
EndSelect
EndProcedure
; - Connect some Event Handlers
App::ConnectEventHandler(App::#Event_Initialise, @OnEvent())
App::ConnectEventHandler(App::#Event_Release, @OnEvent())
App::ConnectEventHandler(#Event_Custom, @OnEvent())
; - Create a loop function that executes every x milliseconds
App::RegisterInterval(@OnInterval(), 750, @Interval)
; - Start Main Loop
App::Execute()
Debug "Execution Ended"

P.S: This is going to be included in the GitHub repository you can access freely by following the link in my signature.