Multiple WaitWindowEvent() in a single project (via include/procedure)?

Just starting out? Need help? Post your questions and find answers here.
Nudgy
User
User
Posts: 10
Joined: Mon May 27, 2024 8:11 pm

Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by Nudgy »

Hi,

As far as I understand, there can (typically?) be only one WaitWindowEvent() loop in a one application. In other words, all window events must usually be handled in the main event loop of your code. But are there any workarounds for this?

Example: There is one, common child window/form that I want to use in multiple, different projects via an "include file". This include file would contain a procedure - for example "ShowMyCommonWindow1(#ParentFormID)" - which could be called to temporarily disable the parent form (mimicking a modal window) and open the child window. The include file would contain its own WaitWindowEvent() loop and would return the results of the window/form. To make this as modular as possible and minimize manual event loop coding, I would like the event handling for the child window to be handled not in the "main" event loop, but in the include file, so that the main code only needs to call ShowMyCommonWindow1() and more or less nothing else.

Possibly, it's the same topic as described in this forum post topic from 20 years ago, but with no clear solution provided: viewtopic.php?t=9073

Any ideas - or is this impossible to accomplish?

Thanks.
wombats
Enthusiast
Enthusiast
Posts: 716
Joined: Thu Dec 29, 2011 5:03 pm

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by wombats »

Have you looked at BindEvent, etc.? I mainly use these in my project rather than the event loop.
boddhi
Enthusiast
Enthusiast
Posts: 524
Joined: Mon Nov 15, 2010 9:53 pm

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by boddhi »

Nudgy wrote: Any ideas - or is this impossible to accomplish?
Hello,

It's totally possible.
The procedure is as follows: From the parent source code, you call a procedure in your include file that creates your modal window, and within it, you either create your new event loop or create a new procedure, always in your include file, that will manage the child window's events until it closes.
The only precaution to take is that the child window's gadgets must not have the same ID as those in the main window.
If possible, use the 'Enumeration' command, followed by a same name for your main file and your includes. This avoids overlapping of assigned values.

Code: Select all

Enumeration Gadgets
  ...
  ...
EndEnumeration
is a good example ; :wink:
If my English syntax and lexicon are incorrect, please bear with Google translate and DeepL. They rarely agree with each other!
Except on this sentence...
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by TI-994A »

Nudgy wrote: Wed Aug 07, 2024 8:17 pm... I would like the event handling for the child window to be handled not in the "main" event loop, but in the include file, so that the main code only needs to call ShowMyCommonWindow1() and more or less nothing else. ...
Technically speaking, implementing multiple event loops is not wrong, although it's considered a poor design paradigm. All system events should ideally be routed through a centralised handler, and any additional components should be designed for modular integration into the main code.

Here's a simple working example to demonstrate this, and it also satisfies your plug&play requirement:

the main window file:

Code: Select all

EnableExplicit

Enumeration windows
  #mainWindow
EndEnumeration

Enumeration timers
  #mainWindowTimer
EndEnumeration

Enumeration gadgets
  #mainOpenModalButton
  #mainEditor
  #mainEditorLabel
  #mainQuitButton
  #mainTimeLabel
  #mainTimeDisplay  
EndEnumeration

Define wFlags, event, appQuit, modalWindow1


;###### include the modal window code ######

     IncludeFile "myCommonWindow1.pbi"

;###########################################


wFlags = #PB_Window_SystemMenu | #PB_Window_ScreenCentered
OpenWindow(#mainWindow, #PB_Ignore, #PB_Ignore, 800, 500, 
           "A Multi-Window Handler Example", wFlags)
TextGadget(#mainTimeLabel, 630, 20, 60, 30, "Time: ")
TextGadget(#mainTimeDisplay, 700, 20, 80, 30, "00:00:00")
TextGadget(#mainEditorLabel, 40, 60, 400, 30, 
           "Text returned from the modal window:")
EditorGadget(#mainEditor, 40, 100, 720, 300)
ButtonGadget(#mainOpenModalButton, 40, 440, 250, 30, "SHOW MODAL WINDOW")
ButtonGadget(#mainQuitButton, 560, 440, 200, 30, "QUIT")
AddWindowTimer(#mainWindow, #mainWindowTimer, 1000)


;##################### add the modal window ######################

   modalWindow1 = initMyCommonWindow1(#mainWindow, #mainEditor)

;#################################################################


Repeat
  event = WaitWindowEvent()  
  Select EventWindow()
      
    Case #MainWindow
      Select event
          
        Case #PB_Event_CloseWindow
          Select EventWindow()
              
            Case #mainWindow
              appQuit = #True  
              
          EndSelect
          
        Case #PB_Event_Timer          
          Select EventTimer()  
              
            Case #mainWindowTimer
              SetGadgetText(#mainTimeDisplay, 
                            FormatDate("%hh:%ii:%ss", Date()))             
              
          EndSelect                                            
          
        Case #PB_Event_Gadget
          Select EventGadget()
              
              
;############## show the modal window ###############
              
            Case #mainOpenModalButton
              showMyCommonWindow1()              
              
;####################################################  
              
              
            Case #mainQuitButton
              appQuit = #True
              
          EndSelect
          
      EndSelect      
      

;###### relay all the modal window events ######
      
    Case modalWindow1
      processMyCommonWindow1Events(event)      
      
;###############################################
      
      
  EndSelect
  
Until appQuit

the modal window include file (myCommonWindow1.pbi):

Code: Select all

Enumeration windows
  #modal1Window  
EndEnumeration

Enumeration timers
  #modal1Timer
EndEnumeration

Enumeration gadgets
  #modal1TimeLabel
  #modal1TimeDisplay
  #modal1Editor
  #modal1EditorLabel
  #modal1SayHelloButton
  #modal1RelayToParentWindow
  #modal1QuitButton 
EndEnumeration

Define parentWindow = -1, relayGadget = -1

Macro prompt(window, message)
  MessageRequester(window, message, #PB_MessageRequester_Ok)
EndMacro

Procedure initMyCommonWindow1(parentWindowNumber, relayGadgetNumber) 
  Shared parentWindow, relayGadget
  parentWindow = parentWindowNumber
  relayGadget = relayGadgetNumber
  ProcedureReturn #modal1Window
EndProcedure

Procedure showMyCommonWindow1()
  Shared parentWindow
  Define wFlags = #PB_Window_SystemMenu | #PB_Window_WindowCentered   
  DisableWindow(parentWindow, #True)
  OpenWindow(#modal1Window, #PB_Ignore, #PB_Ignore, 600, 300, "My Common Window 1", 
             wFlags, WindowID(parentWindow))
  TextGadget(#modal1TimeLabel, 430, 20, 60, 30, "Time: ")
  TextGadget(#modal1TimeDisplay, 500, 20, 80, 30, "00:00:00")
  TextGadget(#modal1EditorLabel, 20, 50, 250, 30, "Type something:")
  EditorGadget(#modal1Editor, 20, 80, 560, 150)
  ButtonGadget(#modal1SayHelloButton, 20, 250, 150, 30, "SAY HELLO!")
  ButtonGadget(#modal1QuitButton, 430, 250, 150, 30, "CLOSE ME!")
  ButtonGadget(#modal1RelayToParentWindow, 200, 250, 200, 30, "RELAY TO PARENT")  
  AddWindowTimer(#modal1Window, #modal1Timer, 1000)
  
EndProcedure

Procedure processMyCommonWindow1Events(event)  
  Shared parentWindow, relayGadget
  
  Select event                
      
    Case #PB_Event_CloseWindow
      DisableWindow(parentWindow, #False)
      CloseWindow(EventWindow())
      
    Case #PB_Event_Timer
      
      Select EventTimer()
          
        Case #modal1Timer
          SetGadgetText(#modal1TimeDisplay, 
                        FormatDate("%hh:%ii:%ss", Date()))
          
      EndSelect
      
    Case #PB_Event_Gadget
      Select EventGadget()
          
        Case #modal1SayHelloButton   
          prompt(GetWindowTitle(EventWindow()), "Modal Window 1 says Hello!")                                          
          
        Case #modal1RelayToParentWindow
          If relayGadget <> -1 And IsGadget(relayGadget)
            SetGadgetText(relayGadget, GetGadgetText(#modal1Editor)) 
          Else
            prompt(GetWindowTitle(EventWindow()), "No relay gadget initialised!")
          EndIf
                    
        Case #modal1QuitButton           
          DisableWindow(parentWindow, #False)
          CloseWindow(EventWindow())
          
      EndSelect
      
  EndSelect  
EndProcedure

The modal window could be easily integrated into any main code in four simple steps:
1. include the modal window code:
> IncludeFile "myCommonWindow1.pbi"

2. initialise the window (with one optional parameter):
> modalWindow1 = initMyCommonWindow1(#mainWindow [, #mainEditor])

3. call the modal window whenever required:
> showMyCommonWindow1()

4. intercept and relay the modal window events back to it:
> processMyCommonWindow1Events(event)


The modal window include file is simply dropped in without requiring any modifications to its code. The main window has no visibility of the gadgets within the modal window, nor does it process any of its events. The main event loop will simply relay all events triggered by the modal window back to it to be processed autonomously.

This example includes a simple inter-window function which relays any text typed into the modal window editor to the main window editor. If no relay gadget is initialised (initMyCommonWindow1(#mainWindow, -1)), the relay function will be ignored. Again, this approach is just a POC - there are many other ways to achieve this.

I hope this helps. :D
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
User avatar
NicTheQuick
Addict
Addict
Posts: 1503
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by NicTheQuick »

I also would use BindEvent and BindGadgetEvent. Together with #PB_Any for creating dynamic Window and Gadget IDs you can make your modal window completely independent from the main event loop. You can even use it to create multiple windows of the same type.

I once recorded a Youtube video explaining all that on an example but it is in German. So it won't make sense to link here I guess.
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
mk-soft
Always Here
Always Here
Posts: 6202
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by mk-soft »

NicTheQuick wrote: Thu Aug 08, 2024 10:09 am I once recorded a Youtube video explaining all that on an example but it is in German. So it won't make sense to link here I guess.
Why not? There are also many people here from German.

Another method is with BindEvent(#PB_Event_Gadget, @DoEventAllGadget()) and with Enumeration Gadgets and Virtual Tables.
However, this is somewhat more complex to implement. (Method from EventDesigner V3)
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
Nudgy
User
User
Posts: 10
Joined: Mon May 27, 2024 8:11 pm

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by Nudgy »

Thanks to both of you for your help.

The hint about enumerations gave me a nudge in the right direction, it seems. I was apparently not creating/assigning unique IDs for my child form, and as a result this was preventing my WaitWindowEvent() loop from executing correctly, since multiple Forms/gadgets had the same IDs.

I can prevent the issue by assigning the child form and its gadgets enumeration numbers starting at e.g. 2000 (e.g. "Enumeration 2000"), although I understand that such a solution will use slightly more memory than strictly necessary, and there is a risk that I again get non-unique IDs/constants if a project exceeds that number of IDs or uses the same range.

Do you perhaps know of a solution that would ensure a unique enumeration constant/ID no matter which project the child window is called from (if not using #PB_Any)? My challenge is that different projects may contain several different named or unnamed enumerations. For example, is there constant similar to "#PB_Compiler_EnumerationValue", which contains the next/lowest, unused ID across all named and unnamed enumerations?

(Edit: When I posted this response I had only seen two responses, but it seems several of you have answered now. Thank you all for your suggestions. I have some reading and thinking to do! :) )
User avatar
NicTheQuick
Addict
Addict
Posts: 1503
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by NicTheQuick »

Just use #PB_Any, seriously. I don't understand why you want to use constants for that.
mk-soft wrote: Thu Aug 08, 2024 10:32 am
NicTheQuick wrote: Thu Aug 08, 2024 10:09 am I once recorded a Youtube video explaining all that on an example but it is in German. So it won't make sense to link here I guess.
Why not? There are also many people here from German.
Here you go: https://www.youtube.com/watch?v=eYrBLy13y2Q :wink:
And wow, it's already 10 years old. Time flies...
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
mk-soft
Always Here
Always Here
Posts: 6202
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by mk-soft »

Nudgy wrote: Thu Aug 08, 2024 11:11 am ...
Do you perhaps know of a solution that would ensure a unique enumeration constant/ID no matter which project the child window is called from (if not using #PB_Any)? My challenge is that different projects may contain several different named or unnamed enumerations. For example, is there constant similar to "#PB_Compiler_EnumerationValue", which contains the next/lowest, unused ID across all named and unnamed enumerations?
...
The solution is Named Enumeration. Without it, it becomes too complex
See PB Help: https://www.purebasic.com/documentation ... tions.html
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: 798
Joined: Wed Dec 31, 2008 3:36 pm

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by Axolotl »

With a well thought-out design there is no reason not to use WaitWindowEvent() and WindowEvent() more than once.
It is important to understand that the message queue (of the system) is queried by WaitWindowEvent() and WindowEvent(). This means that if a message is read it is removed from the message queue (default behavior) with WaitWindowEvent() or WindowEvent(), and it should also be processed.

Maybe this gives you some ideas: Handling Events - two different ways
BTW: The Windows API knows the PeekMessage_() function, and the explanations about the usage are very good to understand the underlaying concepts of the windows message queue.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by jacdelad »

Mr. Axolotl,
I respectfully disagree. In my programs I sometimes use multiple event loops: When the main window is disabled and another window is open, like settings/preferences, I use a separate loop just for this window. This way I don't get a monstrum of main loop and the things must happen anyway are doing their thing using Bind...(). But this heavily depends on the program and requires some discipline, of course!
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
User avatar
TI-994A
Addict
Addict
Posts: 2698
Joined: Sat Feb 19, 2011 3:47 am
Location: Singapore
Contact:

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by TI-994A »

Since many are suggesting the use of #PB_Any and the BindEvent() function, here's my earlier-posted code, free of enumerations and conventional message loops. Instead of binding each gadget and event individually, it's all bound and processed by consolidated handlers.

the main window file:

Code: Select all

EnableExplicit

Global mainWindow, mainTimeLabel, mainTimeDisplay, mainEditorLabel, 
       mainEditor, mainOpenModalButton, mainQuitButton, mainWindowTimer = 0

IncludeFile "myCommonWindow1.pbi"

Procedure mainGadgetHandler()
  Select EventGadget()      
    Case mainOpenModalButton      
      showMyCommonWindow1(mainWindow, mainEditor)      
    Case mainQuitButton
      CloseWindow(mainWindow)
      End
  EndSelect    
EndProcedure

Procedure mainTimerHandler()
  Select EventTimer()          
    Case modal1Timer
      SetGadgetText(mainTimeDisplay, 
                    FormatDate("%hh:%ii:%ss", Date()))          
  EndSelect  
EndProcedure

Procedure mainCloseWindowHandler()    
  CloseWindow(mainWindow)
  End
EndProcedure

mainWindow = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 
                        800, 500, "A Multi-Window Handler Example",
                        #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
mainTimeLabel = TextGadget(#PB_Any, 630, 20, 60, 30, "Time: ")
mainTimeDisplay = TextGadget(#PB_Any, 700, 20, 80, 30, "00:00:00")
mainEditorLabel = TextGadget(#PB_Any, 40, 60, 400, 30, 
                             "Text returned from the modal window:")
mainEditor = EditorGadget(#PB_Any, 40, 100, 720, 300)
mainOpenModalButton = ButtonGadget(#PB_Any, 40, 440, 250, 30, "SHOW MODAL WINDOW")
mainQuitButton = ButtonGadget(#PB_Any, 560, 440, 200, 30, "QUIT")
AddWindowTimer(mainWindow, mainWindowTimer, 1000)

BindEvent(#PB_Event_Gadget, @mainGadgetHandler(), mainWindow, #PB_All, #PB_All)
BindEvent(#PB_Event_Timer, @mainTimerHandler(), mainWindow, #PB_All, #PB_All)
BindEvent(#PB_Event_CloseWindow, @mainCloseWindowHandler(), mainWindow, #PB_All, #PB_All)

While WaitWindowEvent() ! #PB_Event_CloseWindow : Wend

the modal window include file (myCommonWindow1.pbi):

Code: Select all

Global modal1Window, modal1TimeLabel, modal1TimeDisplay, modal1EditorLabel, modal1Editor,
       modal1SayHelloButton, modal1QuitButton, modal1RelayToParentWindow, modal1Timer = 0,
       parentWindow, relayGadget = -1

Macro prompt(window, message)
  MessageRequester(window, message, #PB_MessageRequester_Ok)
EndMacro

Procedure modal1GadgetHandler()
  Select EventGadget()
      
    Case modal1SayHelloButton   
      prompt(GetWindowTitle(EventWindow()), "Modal Window 1 says Hello!")                                          
      
    Case modal1RelayToParentWindow
      If relayGadget <> -1 And IsGadget(relayGadget)
        SetGadgetText(relayGadget, GetGadgetText(modal1Editor)) 
      Else
        prompt(GetWindowTitle(EventWindow()), "No relay gadget initialised!")
      EndIf
      
    Case modal1QuitButton           
      DisableWindow(parentWindow, #False)
      CloseWindow(EventWindow())
      
  EndSelect    
EndProcedure

Procedure modal1TimerHandler()
  Select EventTimer()          
    Case modal1Timer
      SetGadgetText(modal1TimeDisplay, 
                    FormatDate("%hh:%ii:%ss", Date()))          
  EndSelect  
EndProcedure

Procedure Modal1CloseWindowHandler()  
  DisableWindow(parentWindow, #False)
  CloseWindow(EventWindow())
EndProcedure

Procedure showMyCommonWindow1(parentWindowNumber, relayGadgetNumber)
  parentWindow = parentWindowNumber
  relayGadget = relayGadgetNumber
  DisableWindow(parentWindow, #True)
  
  modal1Window = OpenWindow(#PB_Any, #PB_Ignore, #PB_Ignore, 600, 300, "My Common Window 1", 
                            #PB_Window_SystemMenu | #PB_Window_WindowCentered, WindowID(parentWindow))
  
  modal1TimeLabel = TextGadget(#PB_Any, 430, 20, 60, 30, "Time: ")
  modal1TimeDisplay = TextGadget(#PB_Any, 500, 20, 80, 30, "00:00:00")
  modal1EditorLabel = TextGadget(#PB_Any, 20, 50, 250, 30, "Type something:")
  modal1Editor = EditorGadget(#PB_Any, 20, 80, 560, 150)
  modal1SayHelloButton = ButtonGadget(#PB_Any, 20, 250, 150, 30, "SAY HELLO!")
  modal1QuitButton = ButtonGadget(#PB_Any, 430, 250, 150, 30, "CLOSE ME!")
  modal1RelayToParentWindow = ButtonGadget(#PB_Any, 200, 250, 200, 30, "RELAY TO PARENT")  
  AddWindowTimer(modal1Window, modal1Timer, 1000)
  
  BindEvent(#PB_Event_Gadget, @modal1GadgetHandler(), modal1Window, #PB_All, #PB_All)
  BindEvent(#PB_Event_Timer, @modal1TimerHandler(), modal1Window, #PB_All, #PB_All)
  BindEvent(#PB_Event_CloseWindow, @modal1CloseWindowHandler(), modal1Window, #PB_All, #PB_All)
  
EndProcedure

The modal window is now called in two short steps:
1. IncludeFile "myCommonWindow1.pbi"
3. showMyCommonWindow1(parentWindow [, relayGadget])

That's all! :wink:
Texas Instruments TI-99/4A Home Computer: the first home computer with a 16bit processor, crammed into an 8bit architecture. Great hardware - Poor design - Wonderful BASIC engine. And it could talk too! Please visit my YouTube Channel :D
Axolotl
Enthusiast
Enthusiast
Posts: 798
Joined: Wed Dec 31, 2008 3:36 pm

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by Axolotl »

Hi jacdelad
jacdelad wrote: Thu Aug 08, 2024 3:13 pm Mr. Axolotl,
I respectfully disagree. ....
That's totally okay with me.
Everyone has their own opinion and in the end, success proves them right.
BTW: I cannot find any contradiction in your statement to my own.
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
jacdelad
Addict
Addict
Posts: 1991
Joined: Wed Feb 03, 2021 12:46 pm
Location: Riesa

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by jacdelad »

My apologies, Mr. Axolotl, I oversaw the "not" in your first senctence!
Good morning, that's a nice tnetennba!

PureBasic 6.21/Windows 11 x64/Ryzen 7900X/32GB RAM/3TB SSD
Synology DS1821+/DX517, 130.9TB+50.8TB+2TB SSD
Axolotl
Enthusiast
Enthusiast
Posts: 798
Joined: Wed Dec 31, 2008 3:36 pm

Re: Multiple WaitWindowEvent() in a single project (via include/procedure)?

Post by Axolotl »

No need to apologize.
I had already thought that. :D
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
Post Reply