Computer programs typically run on some form of operating system, and the operating system relays all the inputs and outputs between the user and the application itself. The operating system notifies each application when there are inputs intended for the application so that the inputs can be consumed and processed accordingly. This notification data is referred to as messages, and most applications run a dedicated loop to continuously await such messages. This dedicated loop is also known as the message loop, message pump, or the message dispatcher. It dispatches each relevant message that is received to the corresponding handler.
PureBasic's Event Loop
In PureBasic, system messages are intercepted by two native functions; WindowEvent() and WaitWindowEvent(). They are typically called in a loop known as the main event loop, which looks something like this:
Code: Select all
OpenWindow(0, 0, 0, 300, 400,
"The Windows Message Loop",
#PB_Window_SystemMenu |
#PB_Window_SizeGadget |
#PB_Window_ScreenCentered)
ListViewGadget(0, 10, 10, 280, 380) ; for status output only
; - this is the conventional event loop, also known as
; the message loop, message pump, or message dispatcher
; - it acts like a post office receiving event messages for
; its application and dispatching them to relevant handlers
; - here is a cross sampling of some of the event messages that the
; application expects and how they will be dispatched and processed
Repeat
event = WaitWindowEvent()
Select event
Case #PB_Event_SizeWindow
; to handle window resizing events
AddGadgetItem(0, 0, "window resized...")
Case #PB_Event_MoveWindow
; to handle window move events
AddGadgetItem(0, 0, "window moving...")
Case #PB_Event_CloseWindow
; to handle the window close event
AddGadgetItem(0, 0, "window closing...")
Case #PB_Event_LeftClick
; to handle a left mouse click on the window
AddGadgetItem(0, 0, "left mouse click on window...")
Case #PB_Event_RightClick
; to handle a right mouse click on the window
AddGadgetItem(0, 0, "right mouse click on window...")
Case #PB_Event_Gadget
; to handle all gadget events
AddGadgetItem(0, 0, "unspecified gadget event...")
Case #PB_Event_Timer
; to handle all timer events
AddGadgetItem(0, 0, "left mouse click on window...")
Case #PB_Event_SysTray
; to handle all system tray events
AddGadgetItem(0, 0, "click on the app system tray icon...")
EndSelect
ForEver
PureBasic's Event Binding
PureBasic also offers alternative methods to this conventional event loop, in the form of event binding. Technically, events and objects can be bound to dedicated handlers which would automatically be called when the bound events or objects are triggered. For larger applications, this approach provides more modularity to code design, as opposed to stacking all the event conditions into one huge loop. Once bound, these events and objects do not need to be processed in the main event loop any longer. This approach could conceivably replace the main event loop altogether, but it could still exist side-by-side with bound events, processing other events if required.
1a. BindEvent()
The main binding method is the BindEvent() function, which has the following basic syntax:
Code: Select all
; when specificEvent is triggered > relay it to specificHandler()
BindEvent(specificEvent, @specificHandler())
The events natively supported by PureBasic include the following:
PureBasic Manual wrote: #PB_Event_Menu : a menu has been selected
#PB_Event_Gadget : a gadget has been pushed
#PB_Event_SysTray : an icon in the systray zone was clicked
#PB_Event_Timer : a timer has reached its timeout
#PB_Event_CloseWindow : the window close gadget has been pushed
#PB_Event_Repaint : the window content has been destroyed and must be repainted
#PB_Event_SizeWindow : the window has been resized
#PB_Event_MoveWindow : the window has been moved
#PB_Event_MinimizeWindow : the window has been minimized
#PB_Event_MaximizeWindow : the window has been maximized
#PB_Event_RestoreWindow : the window has been restored to normal size
#PB_Event_ActivateWindow : the window has been activated (got the focus)
#PB_Event_DeactivateWindow : the window has been deactivated (lost the focus)
#PB_Event_WindowDrop : a drag & drop operation was finished on a window
#PB_Event_GadgetDrop : a drag & drop operation was finished on a gadget
#PB_Event_RightClick : a right mouse button click has occurred on the window
#PB_Event_LeftClick : a left mouse button click has occurred on the window
#PB_Event_LeftDoubleClick : a left mouse button double-click has occurred on the window
Technically, these events could be bound to specific handlers to be processed. Here's an example:
Code: Select all
Procedure closeWindowHandler()
Shared window
MessageRequester("PureBasic BindEvent() Function",
"Received the close-window event")
CloseWindow(window)
End
EndProcedure
window = OpenWindow(#PB_Any, 0, 0, 300, 400, "PureBasic BindEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler())
Repeat : WaitWindowEvent() : ForEver
In the above example, the #PB_Event_CloseWindow event is bound to the closeWindowHandler() procedure to exclusively process the window-close event, and nothing else. Here, the handler simply displays a message box before ending the program; but if more logic were required prior to closing the window, it could all be consolidated into this one single procedure. Of course, the main event loop could also simply call a dedicated procedure in the same manner; but with this approach, there are some improved design and performance considerations, and it provides the option to forgo the main event loop altogether, if so desired.
It should also be noted that in the context of the above example, if there is more than one active window, the close window-events for all of them will be bound to the closeWindowHandler() procedure. The next example demonstrates this:
Code: Select all
Procedure closeWindowHandler()
Shared window1, window2, appQuit
Select EventWindow() ; determine the event window
Case window1
If MessageRequester("PureBasic BindEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(window1)
End
EndIf
Case window2
If MessageRequester("PureBasic BindEvent() Function", "Close the child window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(window2)
EndIf
EndSelect
EndProcedure
window1 = OpenWindow(#PB_Any, 0, 0, 600, 400, "PureBasic BindEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
window2 = OpenWindow(#PB_Any, 0, 0, 300, 200, "Child window...",
#PB_Window_SystemMenu | #PB_Window_WindowCentered,
WindowID(window1))
; binds the close window event for all windows to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler())
Repeat : WaitWindowEvent() : ForEver
1b. BindEvent()
Alternatively, the BindEvent() function could also be configured to bind events based on specific windows.
This is done with the third parameter of the function call:
Code: Select all
; when specificEvent is triggered by specificWindow > relay it to specificHandler()
BindEvent(specificEvent, @specificHandler(), specificWindow)
With the window parameter of the BindEvent() function set, only events from the specific window will trigger its corresponding handler, like this:
Code: Select all
Procedure closeWindowHandler1()
If MessageRequester("PureBasic BindEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure closeWindowHandler2()
If MessageRequester("PureBasic BindEvent() Function", "Close the child window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
EndIf
EndProcedure
window1 = OpenWindow(#PB_Any, 0, 0, 600, 400, "PureBasic BindEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
window2 = OpenWindow(#PB_Any, 0, 0, 300, 200, "Child window...",
#PB_Window_SystemMenu | #PB_Window_WindowCentered,
WindowID(window1))
; binds the close window event of only window1 to closeWindowHandler1()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler1(), window1)
; binds the close window event of only window2 to closeWindowHandler2()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler2(), window2)
Repeat : WaitWindowEvent() : ForEver
Another event that would require handling of multiple objects is the #PB_Event_Gadget event, which could be triggered by any of the active gadgets in an application. The handling would be quite similar to the handling of multiple windows, as demonstrated here:
Code: Select all
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure gadgetHandler()
; to handle all the gadget events
Shared button, editor1, editor2
Define event.s, editor = editor1
Select EventGadget() ; determine the event gadget
Case editor1 ; contains three associated event types
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "Editor 1 received focus!"
Case #PB_EventType_LostFocus
event = "Editor 1 lost focus!"
Case #PB_EventType_Change
event = "Editor 1 contents changed!"
EndSelect
Case editor2 ; contains three associated event types
editor = editor2
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "Editor 2 received focus!"
Case #PB_EventType_LostFocus
event = "Editor 2 lost focus!"
Case #PB_EventType_Change
event = "Editor 2 contents changed!"
EndSelect
Case button ; no associated event types
event = "Button clicked!"
EndSelect
AddGadgetItem(editor, 0, event)
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 450, "PureBasic BindEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor1 = EditorGadget(#PB_Any, 10, 10, 580, 180)
editor2 = EditorGadget(#PB_Any, 10, 200, 580, 180)
button = ButtonGadget(#PB_Any, 200, 390, 200, 40, "Button")
; binds the close window event to closeWindowHandler()
; only 1 window - so no window parameter required
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler())
; binds all gadget events to gadgetHandler()
; only 1 window - so no window parameter required
BindEvent(#PB_Event_Gadget, @gadgetHandler())
Repeat : WaitWindowEvent() : ForEver
In the above example, the gadget handler must process all the events for all the gadgets. It does this by determining the gadget that triggered the event using the EventGadget() function. However, notice that some events here are further processed for an event type. Unlike window objects, some gadgets have event types associated with them. For example, button gadgets have no event types associated with them, so they require no further event-processing. Editor gadgets, however, have three associated event types, and thus, they are subject to another level of conditional checks.
1c. BindEvent()
For greater streamlining and modular control, the BindEvent() function could be further configured to bind events based on specific objects and their associated event types as well.
To bind an event to a specific object, the object must be specified in the fourth parameter of the BindEvent() function call:
Code: Select all
; when specificEvent is triggered by specificObject in specificWindow > relay it to specificHandler()
BindEvent(specificEvent, @specificHandler(), specificWindow, specificObject)
In PureBasic, depending on the event being bound, objects could be gadgets, timers, system tray items, or menu items. The next example demonstrates this object binding:
Code: Select all
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure editorHandler()
; to handle all the events of editor1 only
Shared editor1
Define.s event.s
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "received focus!"
Case #PB_EventType_LostFocus
event = "lost focus!"
Case #PB_EventType_Change
event = "contents changed!"
EndSelect
AddGadgetItem(editor1, 0, "Editor 1 " + event)
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 400, "PureBasic BindEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor1 = EditorGadget(#PB_Any, 10, 10, 580, 180)
editor2 = EditorGadget(#PB_Any, 10, 200, 580, 180)
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler(), window)
; binds all the events of only editor1 to editorHandler()
BindEvent(#PB_Event_Gadget, @editorHandler(), window, editor1)
Repeat : WaitWindowEvent() : ForEver
In the example above, the #PB_Event_Gadget event was bound to the editorHandler() procedure to exclusively handle all the events triggered by only one of the editor gadgets. Notice that the events of the second editor gadget are not being processed, because the handler was expressly bound only to the events of the first editor gadget - editor1.
1d. BindEvent()
Notice also that the handler is processing all the event types triggered by the editor gadget; namely the focus, lost-focus, and the change event types. As mentioned earlier, the BindEvent() function could be configured another level further to filter out these event types as well.
This is done with the fifth parameter:
Code: Select all
; when specificEvent and specificEventType are triggered by specificObject in specificWindow > relay it to specificHandler()
BindEvent(specificEvent, @specificHandler(), specificWindow, specificObject, specificEventType)
These are some of the possible event types triggered by PureBasic gadgets:
PureBasic Manual: wrote: #PB_EventType_LeftClick : left mouse button click
#PB_EventType_RightClick : right mouse button click
#PB_EventType_LeftDoubleClick : left mouse button double click
#PB_EventType_RightDoubleClick : right mouse button double click
#PB_EventType_Focus : get the focus
#PB_EventType_LostFocus : lose the focus
#PB_EventType_Change : content change
#PB_EventType_DragStart : the user tries to start a drag & drop operation
Here's the above example modified to individually bind each of the event types of the editor gadget:
Code: Select all
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure editorFocusHandler()
; to handle only the focus event of editor1
Shared editor1
AddGadgetItem(editor1, 0, "Editor 1 received focus!")
EndProcedure
Procedure editorLostFocusHandler()
; to handle only the lost-focus event of editor1
Shared editor1
AddGadgetItem(editor1, 0, "Editor 1 lost focus!")
EndProcedure
Procedure editorChangeHandler()
; to handle only the change event of editor1
Shared editor1
AddGadgetItem(editor1, 0, "Editor 1 contents changed!")
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 400, "PureBasic BindEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor1 = EditorGadget(#PB_Any, 10, 10, 580, 180)
editor2 = EditorGadget(#PB_Any, 10, 200, 580, 180)
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler(), window)
; binds only the focus event of editor1 to editorFocusHandler()
BindEvent(#PB_Event_Gadget, @editorFocusHandler(), window, editor1, #PB_EventType_Focus)
; binds only the lost-focus event of editor1 to editorLostFocusHandler()
BindEvent(#PB_Event_Gadget, @editorLostFocusHandler(), window, editor1, #PB_EventType_LostFocus)
; binds only the change event of editor1 to editorChangeHandler()
BindEvent(#PB_Event_Gadget, @editorChangeHandler(), window, editor1, #PB_EventType_Change)
Repeat : WaitWindowEvent() : ForEver
Now, let's implement them altogether to see how they synergise:
Code: Select all
Global appQuit
Global window1, w1Editor, w1Button1, w1Button2
Global window2, w2Editor, w2Button1, w2Button2
Procedure closeWindowHandler1()
If MessageRequester("PureBasic BindEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
appQuit = #True
EndIf
EndProcedure
Procedure closeWindowHandler2()
If MessageRequester("PureBasic BindEvent() Function", "Close the child window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
EndIf
EndProcedure
Procedure commonGadgetHandler()
; to handle all gadget events from all windows
Define.s window, event
Select EventWindow() ; determine the window
Case window1
window = "Window 1 "
output = w1Editor
Select EventGadget() ; determine the gadget
Case w1Button2
event = "Button 2 clicked!"
Case w1Editor
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "Editor received focus!"
Case #PB_EventType_LostFocus
event = "Editor lost focus!"
Case #PB_EventType_Change
event = "Editor contents changed!"
EndSelect
Default
event = "Unspecified gadget event!"
EndSelect
Case window2
window = "Window 2 "
output = w2Editor
Select EventGadget() ; determine the gadget
Case w2Button2
event = "Button 2 clicked"
Case w2Editor
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "Editor received focus!"
Case #PB_EventType_LostFocus
event = "Editor lost focus!"
Case #PB_EventType_Change
event = "Editor contents changed!"
EndSelect
Default
event = "Unspecified gadget event!"
EndSelect
EndSelect
If EventType() <> #PB_EventType_Change And event <> ""
AddGadgetItem(output, 0, "[common handler] " + window + event)
EndIf
EndProcedure
Procedure win1Button1Handler()
; to handle only button1 from window1
AddGadgetItem(w1Editor, 0, "[win1_but1 handler] Window 1 Button 1 clicked!")
EndProcedure
Procedure win2EditorHandler()
; to handle only the change-event-type of the editor in window2
AddGadgetItem(w2Editor, 0, "[win2_edit handler] Window 2 Editor contents changed!")
EndProcedure
window1 = OpenWindow(#PB_Any, 100, 100, 600, 400,
"PureBasic BindEvent() Function",
#PB_Window_SystemMenu)
w1Editor = EditorGadget(#PB_Any, 10, 10, 580, 330)
w1Button1 = ButtonGadget(#PB_Any, 10, 350, 200, 40, "Button 1")
w1Button2 = ButtonGadget(#PB_Any, 390, 350, 200, 40, "Button 2")
window2 = OpenWindow(#PB_Any, 600, 400, 500, 300, "Child window...",
#PB_Window_SystemMenu, WindowID(window1))
w2Editor = EditorGadget(#PB_Any, 10, 10, 480, 230)
w2Button1 = ButtonGadget(#PB_Any, 10, 250, 150, 40, "Button 1")
w2Button2 = ButtonGadget(#PB_Any, 340, 250, 150, 40, "Button 2")
; binds the close window events of each window to corresponding handlers
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler1(), window1)
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler2(), window2)
; binds all gadget events to commonGadgetHandler()
BindEvent(#PB_Event_Gadget, @commonGadgetHandler())
; bind only button1 from window1 to win1Button1Handler()
BindEvent(#PB_Event_Gadget, @win1Button1Handler(), window1, w1Button1)
; bind only the change-event-type of the editor from window2 to win2EditorHandler()
BindEvent(#PB_Event_Gadget, @win2EditorHandler(), window2, w2Editor, #PB_EventType_Change)
Repeat : WaitWindowEvent() : Until appQuit
To demonstrate the versatility of the binding function, this expanded example above mixes up the bindings with different configurations. The code contains two windows, each with three gadgets. Each window has its own dedicated window-close event handlers, but the gadgets are handled in a very unique manner:
1. all gadget events from both windows are bound to commonGadgetHandler()
2. only the editor gadgets and second buttons from both windows are handled by commonGadgetHandler()
3. the first buttons from either window are not exclusively bound and are not handled by commonGadgetHandler()
4. the first buttons from both windows will still trigger the default response from commonGadgetHandler()
5. the first button from the first window is also exclusively bound to win1Button1Handler() to handle its clicks
6. the editor from the second window is also exclusively bound to win2EditorHandler() to handle only its change events
7. the first button from the first window and the editor change-event from the second window will both trigger twice, once by its own dedicated handlers, and once by commonGadgetHandler() as default, unhandled events.
The commonGadgetHandler() could easily ignore the gadgets and events that it is not handling, but a default handler is added in the example to better demonstrate the mechanics of multiple bindings.
This covers the basics of the BindEvent() function. It is clearly a useful and powerful feature towards cleaner and more efficient code design, especially since it could work side-by-side with duplicate binding calls and with the conventional event handling processes as well.
2. BindGadgetEvent()
It should be noted that PureBasic has another, more specialised binding function, geared exclusively for its gadgets. It is the BindGadgetEvent() function, which works in exactly the same way as the BindEvent() function, but exclusively for the #PB_Event_Gadget event. It could be viewed as an abridged version of the BindEvent() function, not requiring the first Event parameter, nor the third Window parameter.
The BindGadgetEvent() function has the following syntax, with the third parameter as optional:
Code: Select all
; when specificGadget is triggered > relay it to specificHandler()
; or
; when specificEventType of specificGadget is triggered > relay it to specificHandler()
BindGadgetEvent(specificGadget, @specificHandler() [,specificEventType])
Here's an example of the function being implemented without the optional event type parameter (third parameter):
Code: Select all
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindGadgetEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure editor1Handler()
; to handle all editor1 events
Shared editor1
Define event.s
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "Editor 1 received focus!"
Case #PB_EventType_LostFocus
event = "Editor 1 lost focus!"
Case #PB_EventType_Change
event = "Editor 1 contents changed!"
EndSelect
AddGadgetItem(editor1, 0, event)
EndProcedure
Procedure editor2Handler()
; to handle all editor2 events
Shared editor2
Define event.s
Select EventType() ; determine the event type
Case #PB_EventType_Focus
event = "Editor 2 received focus!"
Case #PB_EventType_LostFocus
event = "Editor 2 lost focus!"
Case #PB_EventType_Change
event = "Editor 2 contents changed!"
EndSelect
AddGadgetItem(editor2, 0, event)
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 450, "PureBasic BindGadgetEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor1 = EditorGadget(#PB_Any, 10, 10, 580, 180)
editor2 = EditorGadget(#PB_Any, 10, 200, 580, 180)
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler())
; binds editor1 events to editor1Handler()
BindGadgetEvent(editor1, @editor1Handler())
; binds editor2 events to editor2Handler()
BindGadgetEvent(editor2, @editor2Handler())
Repeat : WaitWindowEvent() : ForEver
The syntax is clearly more succinct when compared to the BindEvent() function. It's even simpler when the event type (third parameter) is specified:
Code: Select all
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindGadgetEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure editorFocusHandler()
; to handle only the focus event of editor1
Shared editor1
AddGadgetItem(editor1, 0, "Editor 1 received focus!")
EndProcedure
Procedure editorLostFocusHandler()
; to handle only the lost-focus event of editor1
Shared editor1
AddGadgetItem(editor1, 0, "Editor 1 lost focus!")
EndProcedure
Procedure editorChangeHandler()
; to handle only the change event of editor1
Shared editor1
AddGadgetItem(editor1, 0, "Editor 1 contents changed!")
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 400, "PureBasic BindGadgetEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor1 = EditorGadget(#PB_Any, 10, 10, 580, 180)
editor2 = EditorGadget(#PB_Any, 10, 200, 580, 180)
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler(), window)
; binds only the focus event of editor1 to editorFocusHandler()
BindGadgetEvent(editor1, @editorFocusHandler(), #PB_EventType_Focus)
; binds only the lost-focus event of editor1 to editorLostFocusHandler()
BindGadgetEvent(editor1, @editorLostFocusHandler(), #PB_EventType_LostFocus)
; binds only the change event of editor1 to editorChangeHandler()
BindGadgetEvent(editor1, @editorChangeHandler(), #PB_EventType_Change)
Repeat : WaitWindowEvent() : ForEver
The above example individually binds the events of the first editor gadget and deliberately leaves the second editor gadget unhandled. Technically, this function works in exactly the same way as the BindEvent() function, but exclusively for the #PB_Event_Gadget event.
3. BindMenuEvent()
Last but not least, there's the BindMenuEvent() function, which also works in exactly the same way as the BindEvent() function, but exclusively for the #PB_Event_Menu event.
The BindMenuEvent() function has the following basic syntax:
Code: Select all
; when specificMenuItem of specificMenu is triggered > relay it to specificHandler()
BindMenuEvent(specificMenu, specificMenuItem, @specificHandler())
Here's a short example:
Code: Select all
Enumeration
#fileMenuItem1 = 1
#fileMenuItem2
#editMenuItem1
#editMenuItem2
#helpMenuItem1
#helpMenuItem2
EndEnumeration
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindMenuEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure menuHandler()
; to handle all the menu items
Shared editor
Define menuEvent.s
Select EventMenu() ; determine the menu item
Case #fileMenuItem1
menuEvent = "File menu item 1 selected!"
Case #fileMenuItem2
menuEvent = "File menu item 2 selected!"
Case #editMenuItem1
menuEvent = "Edit menu item 1 selected!"
Case #editMenuItem2
menuEvent = "Edit menu item 2 selected!"
Case #helpMenuItem1
menuEvent = "Help menu item 1 selected!"
Case #helpMenuItem2
menuEvent = "Help menu item 2 selected!"
EndSelect
AddGadgetItem(editor, 0, menuEvent)
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 400, "PureBasic BindMenuEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor = EditorGadget(#PB_Any, 10, 10, 580, 380)
menu = CreateMenu(#PB_Any, WindowID(window))
MenuTitle("File")
MenuItem(1, "File Item 1")
MenuItem(2, "File Item 2")
MenuTitle("Edit")
MenuItem(3, "Edit Item 1")
MenuItem(4, "Edit Item 2")
MenuTitle("Help")
MenuItem(5, "Help Item 1")
MenuItem(6, "Help Item 2")
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler(), window)
; binds all the menu items to menuHandler()
For i = 1 To 6
BindMenuEvent(menu, i, @menuHandler())
Next i
Repeat : WaitWindowEvent() : ForEver
Similarly, each menu item could have its own handler, like this:
Code: Select all
Enumeration
#fileMenuItem1 = 1
#fileMenuItem2
#fileMenuQuit
#editMenuItem1
#editMenuItem2
#helpMenuItem1
#helpMenuItem2
EndEnumeration
Procedure closeWindowHandler()
If MessageRequester("PureBasic BindMenuEvent() Function", "Close the main window?",
#PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
CloseWindow(EventWindow())
End
EndIf
EndProcedure
Procedure fileMenuHandler()
; to handle the file menu items
Shared editor
Define menuEvent.s
Select EventMenu() ; determine the menu item
Case #fileMenuItem1
menuEvent = "File menu item 1 selected!"
Case #fileMenuItem2
menuEvent = "File menu item 2 selected!"
EndSelect
AddGadgetItem(editor, 0, menuEvent)
EndProcedure
Procedure editMenuHandler()
; to handle the edit menu items
Shared editor
Define menuEvent.s
Select EventMenu() ; determine the menu item
Case #editMenuItem1
menuEvent = "Edit menu item 1 selected!"
Case #editMenuItem2
menuEvent = "Edit menu item 2 selected!"
EndSelect
AddGadgetItem(editor, 0, menuEvent)
EndProcedure
Procedure helpMenuHandler1()
; to exclusively handle the help menu item 1
Shared editor
AddGadgetItem(editor, 0, "Help menu item 1 selected!")
EndProcedure
Procedure helpMenuHandler2()
; to exclusively handle the help menu item 2
Shared editor
AddGadgetItem(editor, 0, "Help menu item 2 selected!")
EndProcedure
window = OpenWindow(#PB_Any, 100, 100, 600, 400, "PureBasic BindMenuEvent() Function",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
editor = EditorGadget(#PB_Any, 10, 10, 580, 380)
menu = CreateMenu(#PB_Any, WindowID(window))
MenuTitle("File")
MenuItem(1, "File Item 1")
MenuBar()
MenuItem(2, "File Item 2")
MenuBar()
MenuItem(3, "Quit")
MenuTitle("Edit")
MenuItem(4, "Edit Item 1")
MenuBar()
MenuItem(5, "Edit Item 2")
MenuTitle("Help")
MenuItem(6, "Help Item 1")
MenuBar()
MenuItem(7, "Help Item 2")
; binds the close window event to closeWindowHandler()
BindEvent(#PB_Event_CloseWindow, @closeWindowHandler(), window)
; binds the file menu > quit item to closeWindowHandler()
BindEvent(#PB_Event_Menu, @closeWindowHandler(), window, #fileMenuQuit)
; binds the file menu items to fileMenuHandler()
BindMenuEvent(menu, #fileMenuItem1, @fileMenuHandler())
BindMenuEvent(menu, #fileMenuItem2, @fileMenuHandler())
; binds the edit menu items to editMenuHandler()
BindMenuEvent(menu, #editMenuItem1, @editMenuHandler())
BindMenuEvent(menu, #editMenuItem2, @editMenuHandler())
; binds the help menu > item 1 to helpMenuHandler1()
BindMenuEvent(menu, #helpMenuItem1, @helpMenuHandler1())
; binds the help menu > item 2 to helpMenuHandler2()
BindMenuEvent(menu, #helpMenuItem2, @helpMenuHandler2())
Repeat : WaitWindowEvent() : ForEver
For demonstration purposes, the above example has the following arbitrary scenarios:
1. both the file menu items are bound to a single handler, fileMenuHandler()
2. both the edit menu items are bound to a single handler, editMenuHandler()
3. the first help menu item is exclusively bound to its own handler, helpMenuHandler1()
4. the second help menu item is exclusively bound to its own handler, helpMenuHandler2()
5. the third file menu item (Quit) is bound to closeWindowHandler() with BindEvent() instead of BindMenuEvent()
And that covers about all of it! Quite simple and straightforward.
While the conventional event loop is still quite efficient and sufficient, an overly large stacked loop could sometimes prove to be a little overwhelming. These binding functions provide good alternatives for modular code design, promoting better readability and maintainability.
I hope that this short tutorial has been helpful in explaining the various implementations of these different binding functions and how they could operate together and alongside the conventional event loop as well.
As always, your feedback and comments are most welcome.

* edited for typos