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

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()
ForEver
It definitely does !TI-994A wrote:[...]
Hope it helps.
Code: Select all
Procedure Events_Timer()
...
EndProcedure
BindEvent(#PB_Event_Timer,@Events_Timer())
I'm glad it helps.Blue wrote:...coding the child window so that it closes the moment the user clicks outside its boundaries...
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()
ForEver
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)
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
EndIf
Code: 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
EndProcedure
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
Code: Select all
If EventGadget() = 2
If IsWindow(1)
CloseWindow(1) ; <<< work around for memory leak ?
EndIf
Child()
EndIf
Thanks for pointing that out, Fred.Fred wrote:It's not really a leak...
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.
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...
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.
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.