So, my question goes to the.weavster : interesting coding you propose. Now, would you also integrate a clock display to fullu demonstrate your point ? I attempted to do it, but got lost
Child window message loop
Re: Child window message loop
@TI-994A : Adding a clock display, as you did, was perfect to demonstrate your point about timers and events being suspended in the main loop when using a secondary loop.
So, my question goes to the.weavster : interesting coding you propose. Now, would you also integrate a clock display to fullu demonstrate your point ? I attempted to do it, but got lost
I'm just not sure where to fit it.
So, my question goes to the.weavster : interesting coding you propose. Now, would you also integrate a clock display to fullu demonstrate your point ? I attempted to do it, but got lost
PB Forums : Proof positive that 2 heads (or more...) are better than one 
Re: Child window message loop
Trusting that the.weavster wouldn't mind, here's the requested integration, keeping with the original code style and structure (all additional code enclosed in hex delineations):Blue wrote:...to the.weavster : interesting coding you propose. Now, would you also integrate a clock display...
Code: Select all
Enumeration windows
#frmMain
#frmChild
EndEnumeration
Enumeration gadgets
#frmMain_btnChild
#frmChild_btnOK
;#################
#frmMain_txtClock
#frmChild_txtClock
;#################
EndEnumeration
;###################
Enumeration timers
#frmMain_tmrClock
#frmChild_tmrClock
EndEnumeration
;###################
; child window - definition and handlers could be in separate file(s)
Procedure frmChild_Open()
If OpenWindow(#frmChild,#PB_Ignore,#PB_Ignore,200,150,"child",#PB_Window_SystemMenu)
ButtonGadget(#frmChild_btnOK,10,10,150,30,"Show Message")
;########################################################
TextGadget(#frmChild_txtClock,20, 110, 160, 30,
FormatDate("%hh:%ii", Date()), #PB_Text_Right)
AddWindowTimer(#frmChild, #frmChild_tmrClock, 1000)
;########################################################
EndIf
EndProcedure
Procedure frmChild_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmChild_btnOK
MessageRequester("","Hello, World!",0)
EndSelect
EndProcedure
; app's main window
Procedure frmMain_Open()
If OpenWindow(#frmMain,#PB_Ignore,#PB_Ignore,400,300,"parent")
ButtonGadget(#frmMain_btnChild,10,10,150,30,"Show Child")
;############################################################
TextGadget(#frmMain_txtClock,280, 20, 100, 30,
FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
AddWindowTimer(#frmMain, #frmMain_tmrClock, 1000)
;############################################################
EndIf
EndProcedure
Procedure frmMain_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmMain_btnChild
frmChild_Open()
EndSelect
EndProcedure
;#####################################################
Procedure Events_Timer()
Static minuteUpdatedTime.s
nTimer = EventTimer()
Select nTimer
Case #frmMain_tmrClock
;updates every second
SetGadgetText(#frmMain_txtClock,
FormatDate("%hh:%ii:%ss", Date()))
Case #frmChild_tmrClock
;updates every minute
currentTime.s = FormatDate("%hh:%ii", Date())
If minuteUpdatedTime <> currentTime
minuteUpdatedTime = currentTime
SetGadgetText(#frmChild_txtClock, currentTime)
EndIf
EndSelect
EndProcedure
;#####################################################
Procedure Events_Gadget()
nGadget = EventGadget()
nEventType = EventType()
nX = 0 : nY = 0
Select GadgetType(nGadget)
Case #PB_GadgetType_Canvas
nX = GetGadgetAttribute(nGadget,#PB_Canvas_MouseX)
nY = GetGadgetAttribute(nGadget,#PB_Canvas_MouseY)
Case #PB_GadgetType_ListIcon
nY = GetGadgetState(nGadget)
; etc...
EndSelect
Select EventWindow()
Case #frmMain
frmMain_onGadget(nGadget,nEventType,nX,nY)
Case #frmChild
frmChild_onGadget(nGadget,nEventType,nX,nY)
EndSelect
EndProcedure
Procedure Events_CloseWindow()
nWin = EventWindow()
Select nWin
Case #frmMain
End ; closing main window quits app
Default
CloseWindow(nWin)
EndSelect
EndProcedure
BindEvent(#PB_Event_Gadget,@Events_Gadget())
BindEvent(#PB_Event_CloseWindow,@Events_CloseWindow())
;#########################################
BindEvent(#PB_Event_Timer,@Events_Timer())
;#########################################
frmMain_Open()
Repeat
nWait = WaitWindowEvent()
ForEverHope it helps.
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 
Re: Child window message loop
It definitely does !TI-994A wrote:[...]
Hope it helps.
The parts that had me hopelessly stuck were
Code: Select all
Procedure Events_Timer()
...
EndProcedure
BindEvent(#PB_Event_Timer,@Events_Timer())
Am i ever glad you provided that code !
There's just no way i would have figured out by myself how and where to integrate Timer events code into the.weavster's code.
Many thanks, TI-994A. You just added a few more days to my life.
And if I may trouble you a little longer, TI-994A, I'd like to ask you how you would go, still in the.weavster's fashion, about coding the child window so that it closes the moment the user clicks outside its boundaries. The reason I'm asking is that I use that technique a lot in my own stuff, but I always code it through a second dedicated event loop within the child window. I now see why this is not a very good practice. As much as I'd like to get my head into coding child windows along the lines of the.weavster's code, some subtleties still escape me.
PB Forums : Proof positive that 2 heads (or more...) are better than one 
Re: Child window message loop
I'm glad it helps.Blue wrote:...coding the child window so that it closes the moment the user clicks outside its boundaries...
To implement a native and cross-platform approach (tested on Windows & Mac OS-X) to detecting a click outside a window, would be to trap the #PB_Event_DeactivateWindow event, like so (again, the additional code is delineated by hexes):
Code: Select all
Enumeration windows
#frmMain
#frmChild
EndEnumeration
Enumeration gadgets
#frmMain_btnChild
#frmChild_btnOK
#frmMain_txtClock
#frmChild_txtClock
EndEnumeration
Enumeration timers
#frmMain_tmrClock
#frmChild_tmrClock
EndEnumeration
; child window - definition and handlers could be in separate file(s)
Procedure frmChild_Open()
If OpenWindow(#frmChild,#PB_Ignore,#PB_Ignore,200,150,"child",#PB_Window_SystemMenu)
ButtonGadget(#frmChild_btnOK,10,10,150,30,"Show Message")
TextGadget(#frmChild_txtClock,20, 110, 160, 30,
FormatDate("%hh:%ii", Date()), #PB_Text_Right)
AddWindowTimer(#frmChild, #frmChild_tmrClock, 1000)
EndIf
EndProcedure
Procedure frmChild_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmChild_btnOK
MessageRequester("","Hello, World!",0)
EndSelect
EndProcedure
; app's main window
Procedure frmMain_Open()
If OpenWindow(#frmMain,#PB_Ignore,#PB_Ignore,400,300,"parent")
ButtonGadget(#frmMain_btnChild,10,10,150,30,"Show Child")
TextGadget(#frmMain_txtClock,280, 20, 100, 30,
FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
AddWindowTimer(#frmMain, #frmMain_tmrClock, 1000)
EndIf
EndProcedure
Procedure frmMain_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmMain_btnChild
frmChild_Open()
EndSelect
EndProcedure
Procedure Events_Timer()
Static minuteUpdatedTime.s
nTimer = EventTimer()
Select nTimer
Case #frmMain_tmrClock
;updates every second
SetGadgetText(#frmMain_txtClock,
FormatDate("%hh:%ii:%ss", Date()))
Case #frmChild_tmrClock
;updates every minute
currentTime.s = FormatDate("%hh:%ii", Date())
If minuteUpdatedTime <> currentTime
minuteUpdatedTime = currentTime
SetGadgetText(#frmChild_txtClock, currentTime)
EndIf
EndSelect
EndProcedure
Procedure Events_Gadget()
nGadget = EventGadget()
nEventType = EventType()
nX = 0 : nY = 0
Select GadgetType(nGadget)
Case #PB_GadgetType_Canvas
nX = GetGadgetAttribute(nGadget,#PB_Canvas_MouseX)
nY = GetGadgetAttribute(nGadget,#PB_Canvas_MouseY)
Case #PB_GadgetType_ListIcon
nY = GetGadgetState(nGadget)
; etc...
EndSelect
Select EventWindow()
Case #frmMain
frmMain_onGadget(nGadget,nEventType,nX,nY)
Case #frmChild
frmChild_onGadget(nGadget,nEventType,nX,nY)
EndSelect
EndProcedure
Procedure Events_CloseWindow()
nWin = EventWindow()
;###########################################
nEvent = Event()
Select nWin
Case #frmMain
If nEvent = #PB_Event_CloseWindow
End ; closing main window quits app
EndIf
Case #frmChild
If (nEvent = #PB_Event_CloseWindow Or
nEvent = #PB_Event_DeactivateWindow)
CloseWindow(nWin)
EndIf
;###########################################
Default
CloseWindow(nWin)
EndSelect
EndProcedure
BindEvent(#PB_Event_Gadget,@Events_Gadget())
BindEvent(#PB_Event_Timer,@Events_Timer())
BindEvent(#PB_Event_CloseWindow,@Events_CloseWindow())
;##########################################################
BindEvent(#PB_Event_DeactivateWindow,@Events_CloseWindow())
;##########################################################
frmMain_Open()
Repeat
nWait = WaitWindowEvent()
ForEverA workaround would be to implement API callbacks; one for each platform in order to maintain cross-compatibility.
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 
- the.weavster
- Addict

- Posts: 1583
- Joined: Thu Jul 03, 2003 6:53 pm
- Location: England
Re: Child window message loop
I wouldn't drop down into the OS API, just use the window's data flag to say whether it should close or not when it's deactivated:TI-994A wrote:One drawback, however, is that the child window is also closed when any other window gets focus; even when its own message box pops open. Harakiri!![]()
A workaround would be to implement API callbacks; one for each platform in order to maintain cross-compatibility.
Code: Select all
Enumeration windows
#frmMain
#frmChild
EndEnumeration
Enumeration gadgets
#frmMain_btnChild
#frmChild_btnOK
#frmMain_txtClock
#frmChild_txtClock
EndEnumeration
Enumeration timers
#frmMain_tmrClock
#frmChild_tmrClock
EndEnumeration
; child window - definition and handlers could be in separate file(s)
Procedure frmChild_Open()
If OpenWindow(#frmChild,#PB_Ignore,#PB_Ignore,200,150,"child",#PB_Window_SystemMenu)
ButtonGadget(#frmChild_btnOK,10,10,150,30,"Select Mode")
TextGadget(#frmChild_txtClock,20, 110, 160, 30,
FormatDate("%hh:%ii", Date()), #PB_Text_Right)
AddWindowTimer(#frmChild, #frmChild_tmrClock, 1000)
;#############################
SetWindowData(#frmChild,#False) ; when #True deactivating the window closes it
ResizeWindow(#frmChild,WindowX(#frmMain)-50,WindowY(#frmMain)-50,#PB_Ignore,#PB_Ignore) ;so we can always see it
SetWindowTitle(#frmChild,"F")
;#############################
EndIf
EndProcedure
Procedure frmChild_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmChild_btnOK
;##############################
nResponse = MessageRequester("","Deactivate closes child?",#PB_MessageRequester_YesNo)
If nResponse = #PB_MessageRequester_No
SetWindowData(#frmChild,#False) ; prevent the window closing
SetWindowTitle(#frmChild,"F")
Else
SetWindowData(#frmChild,#True)
SetWindowTitle(#frmChild,"T")
EndIf
;##############################
EndSelect
EndProcedure
; app's main window
Procedure frmMain_Open()
If OpenWindow(#frmMain,#PB_Ignore,#PB_Ignore,400,300,"parent")
ButtonGadget(#frmMain_btnChild,10,10,150,30,"Show Child")
TextGadget(#frmMain_txtClock,280, 20, 100, 30,
FormatDate("%hh:%ii:%ss", Date()), #PB_Text_Right)
AddWindowTimer(#frmMain, #frmMain_tmrClock, 1000)
EndIf
EndProcedure
Procedure frmMain_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmMain_btnChild
frmChild_Open()
EndSelect
EndProcedure
Procedure Events_Timer()
Static minuteUpdatedTime.s
nTimer = EventTimer()
Select nTimer
Case #frmMain_tmrClock
;updates every second
SetGadgetText(#frmMain_txtClock,
FormatDate("%hh:%ii:%ss", Date()))
Case #frmChild_tmrClock
;updates every minute
currentTime.s = FormatDate("%hh:%ii", Date())
If minuteUpdatedTime <> currentTime
minuteUpdatedTime = currentTime
SetGadgetText(#frmChild_txtClock, currentTime)
EndIf
EndSelect
EndProcedure
Procedure Events_Gadget()
nGadget = EventGadget()
nEventType = EventType()
nX = 0 : nY = 0
Select GadgetType(nGadget)
Case #PB_GadgetType_Canvas
nX = GetGadgetAttribute(nGadget,#PB_Canvas_MouseX)
nY = GetGadgetAttribute(nGadget,#PB_Canvas_MouseY)
Case #PB_GadgetType_ListIcon
nY = GetGadgetState(nGadget)
; etc...
EndSelect
Select EventWindow()
Case #frmMain
frmMain_onGadget(nGadget,nEventType,nX,nY)
Case #frmChild
frmChild_onGadget(nGadget,nEventType,nX,nY)
EndSelect
EndProcedure
;################################
;Split the handlers
Procedure Events_CloseWindow()
nWin = EventWindow()
Select nWin
Case #frmMain
End ; closing main window quits app
Default
CloseWindow(nWin)
EndSelect
EndProcedure
Procedure Events_DeactivateWindow()
nWin = EventWindow()
Select nWin
Case #frmChild ; list all windows you want this behaviour for
If GetWindowData(nWin) = #True
CloseWindow(nWin)
EndIf
EndSelect
EndProcedure
;###############################
BindEvent(#PB_Event_Gadget,@Events_Gadget())
BindEvent(#PB_Event_Timer,@Events_Timer())
BindEvent(#PB_Event_CloseWindow,@Events_CloseWindow())
;###############################
;point this to the new handler
BindEvent(#PB_Event_DeactivateWindow,@Events_DeactivateWindow())
;###############################
frmMain_Open()
Repeat
nWait = WaitWindowEvent()
ForEver
Code: Select all
SetWindowData(#frmChild,#False)
MessageRequester("","Hello, World!",0)
SetWindowData(#frmChild,#True)
Re: Child window message loop
When starting with PB, it's probably a very good advice to use only one event loop.
-
User_Russian
- Addict

- Posts: 1629
- Joined: Wed Nov 12, 2008 5:01 pm
- Location: Russia
Re: Child window message loop
Code: Select all
Procedure Child_Event()
Select Event()
Case #PB_Event_CloseWindow
UnbindEvent(#PB_Event_CloseWindow, @Child_Event(), 1)
CloseWindow(1)
EndSelect
EndProcedure
Procedure Child()
If OpenWindow(1, #PB_Ignore, #PB_Ignore, 200, 150, "child", #PB_Window_SystemMenu, WindowID(0))
BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1)
EndIf
EndProcedure
If OpenWindow(0, #PB_Ignore, #PB_Ignore, 400, 300, "parent")
ButtonGadget(2, 100, 100, 100, 25, "click")
Repeat
Event = WaitWindowEvent()
If EventWindow() = 0
Select Event
Case #PB_Event_Gadget
If EventGadget() = 2
Child()
EndIf
Case #PB_Event_CloseWindow
Break
EndSelect
EndIf
ForEver
EndIfCode: Select all
Procedure Child()
If OpenWindow(1, #PB_Ignore, #PB_Ignore, 200, 150, "child", #PB_Window_SystemMenu, WindowID(0))
BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1)
For i=1 To 1000000
BindEvent(#PB_Event_CloseWindow, @Child_Event(), 1)
Next i
EndIf
EndProcedureRe: Child window message loop
@TI-994A :
Thank you. As I follow along, I'm quickly getting the hang of this. As you demonstrate, #PB_Event_DeactivateWindow appears to be the solution to my requirement. I'll work around that to code specific exceptions, such as NOT letting the child window get deactivated when it opens its own message box !
By the way, your idea of using a text clock to demonstrate that processes are still working is brilliant in its simplicity.
@ the.weavster :
Glad you're back. As you can see, your code is being put to good use; it's turning into a great pedagogical tool. Thank you for that.
I agree with you about avoiding specific OS API calls; as you suggest, it seems possible to determine through flags when exactly to close the child window.
I modified your code slightly (see below) and obtained exactly what I needed. The child window now stays alive when it opens its own message box, but otherwise closes whenever the user clicks outside its boundaries. Thanks for pointing out an intelligent use of the WindowData feature.
@User_Russian :
if you close the child window BEFORE opening it again, as inwon't that stop the memory leak ?
By the way, how do you determine that there's a memory leak ? Through the task manager ?
Thank you. As I follow along, I'm quickly getting the hang of this. As you demonstrate, #PB_Event_DeactivateWindow appears to be the solution to my requirement. I'll work around that to code specific exceptions, such as NOT letting the child window get deactivated when it opens its own message box !
By the way, your idea of using a text clock to demonstrate that processes are still working is brilliant in its simplicity.
@ the.weavster :
Glad you're back. As you can see, your code is being put to good use; it's turning into a great pedagogical tool. Thank you for that.
I agree with you about avoiding specific OS API calls; as you suggest, it seems possible to determine through flags when exactly to close the child window.
I modified your code slightly (see below) and obtained exactly what I needed. The child window now stays alive when it opens its own message box, but otherwise closes whenever the user clicks outside its boundaries. Thanks for pointing out an intelligent use of the WindowData feature.
Code: Select all
;; modified by Blue
Procedure frmChild_Open()
If OpenWindow(#frmChild,#PB_Ignore,#PB_Ignore,200,150,"child",#PB_Window_SystemMenu)
ButtonGadget(#frmChild_btnOK,10,10,150,30,"Message Box") ; <<< changed
TextGadget(#frmChild_txtClock,20, 110, 160, 30,
FormatDate("%hh:%ii", Date()), #PB_Text_Right)
AddWindowTimer(#frmChild, #frmChild_tmrClock, 1000)
;#############################
SetWindowData(#frmChild,#True) ; deactivating the window will close it ; <<< changed
ResizeWindow(#frmChild,WindowX(#frmMain)+80,WindowY(#frmMain)+80,#PB_Ignore,#PB_Ignore) ;so we can always see it
SetWindowTitle(#frmChild,"Closes automatically") ; <<< changed
;#############################
EndIf
EndProcedure
Procedure frmChild_onGadget(nGadget,nEventType,nX,nY)
Select nGadget
Case #frmChild_btnOK
;##############################
SetWindowData(#frmChild,#False)
SetWindowTitle(#frmChild,"Remains active")
nResponse = MessageRequester("Message","something important... or not !",#PB_MessageRequester_Ok)
SetWindowData(#frmChild,#True)
SetWindowTitle(#frmChild,"Closes automatically") ; <<< changed
;##############################
EndSelect
EndProcedure@User_Russian :
if you close the child window BEFORE opening it again, as in
Code: Select all
If EventGadget() = 2
If IsWindow(1)
CloseWindow(1) ; <<< work around for memory leak ?
EndIf
Child()
EndIf
By the way, how do you determine that there's a memory leak ? Through the task manager ?
Last edited by Blue on Mon May 28, 2018 4:57 pm, edited 2 times in total.
PB Forums : Proof positive that 2 heads (or more...) are better than one 
Re: Child window message loop
It's not really a leak, you can bind twice or more the same event. UnbindEvent() will unbind them all.
Re: Child window message loop
Thanks for pointing that out, Fred.Fred wrote:It's not really a leak...
So is there any advantage in taking care to close the window before reopening it ?
PB Forums : Proof positive that 2 heads (or more...) are better than one 
-
User_Russian
- Addict

- Posts: 1629
- Joined: Wed Nov 12, 2008 5:01 pm
- Location: Russia
Re: Child window message loop
Not. CloseWindow does not call UnbindEvent. This is the potential cause of errors that are difficult to detect.Blue wrote:won't that stop the memory leak ?
What is it for necessary? It is difficult for me to imagine in which cases multiple registration of the same event is necessary, related to the same procedure.Fred wrote:It's not really a leak, you can bind twice or more the same event.
Why is UnbindEvent not called from CloseWindow? Why do need to leave an event registration for a window that does not exist? This makes no sense.Fred wrote:UnbindEvent() will unbind them all.
Re: Child window message loop
Always glad it helps.Blue wrote:...work around that to code specific exceptions, such as NOT letting the child window get deactivated when it opens its own message box...
Nevertheless, it should be noted that even windows from other processes would be able to dismiss this child window if they were to automatically pop-up or receive focus.
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 
Re: Child window message loop
@User_Russian
You raise an interesting point here.
I gather from your observations that a memory leak occurs, regardless of its importance or innocuity.
And since CloseWindow does not call UnbindEvent, then it’s up to the programmer to do it.
The question that remains is “is it worth the trouble ?”. Fred seems to be saying “Why bother ?”
You raise an interesting point here.
I gather from your observations that a memory leak occurs, regardless of its importance or innocuity.
And since CloseWindow does not call UnbindEvent, then it’s up to the programmer to do it.
The question that remains is “is it worth the trouble ?”. Fred seems to be saying “Why bother ?”
PB Forums : Proof positive that 2 heads (or more...) are better than one 
Re: Child window message loop
Oops ! That’s an unexpected curve. But now that you point it out, it does seem obvious. Of course, a deactivated window will behave as its deactivation code dictates, regardless of the source of the deactivation. I haven’t a clue how to safeguard against that. But it’s a minor problem, since windows that close when they lose focus are usually only windows where the user enters personalized parameters.TI-994A wrote:[...]
Nevertheless, it should be noted that even windows from other processes would be able to dismiss this child window if they were to automatically pop-up or receive focus.
Just for curiosity’s sake, try the latest updated code provided by the.weavster, but with the 2 modified procedures I submitted. Can you think of an independent process that would interfere unexpectedly with that child window ?
PB Forums : Proof positive that 2 heads (or more...) are better than one 
- the.weavster
- Addict

- Posts: 1583
- Joined: Thu Jul 03, 2003 6:53 pm
- Location: England
Re: Child window message loop
To my mind there is only one event loop in my code. Taking gadget events as an example, Events_Gadget() is the only callback bound to gadget events and that simply routes to other procedures based on the associated window id, gadget id and event type. Would you agree with that, Fred?Fred wrote:When starting with PB, it's probably a very good advice to use only one event loop.
