I have a main GUI which is a framework for various sub-GUIs.
The main GUI listens for ContextMenu events:
Code: Select all
this.MainGui.OnEvent("ContextMenu", (*) => this.ContextMenu.Show())
Code: Select all
this.MainGui.OnEvent("ContextMenu", (*) => this.ContextMenu.Show())
Code: Select all
SubGui.OnEvent("ContextMenu", this.test.Bind(this))
...
test(*)
{
currentMode := A_CoordModeMouse
A_CoordModeMouse := "Screen"
MouseGetPos(&x, &y)
A_CoordModeMouse := currentMode
PostMessage(0x7B, MainGui.Hwnd, (x & 0xFFFFFFFF)|y<<32, MainGui) ; 0x007B->WM_CONTEXTMENU
}
You are passing the coordinates incorrectly (words are 16-bit, not 32-bit). The message handler receives a Y coordinate of zero (assuming (x >> 16) & 0xFFFF = 0), which is very likely to be above the client area of the GUI and therefore assumed to be over the title bar or menu bar.ContextMenu
Launched whenever the user right-clicks anywhere in the window except the title bar and menu bar.
Code: Select all
#Requires AutoHotkey v2.0
class GuiWithButton extends Gui {
__new() {
super.__new(,, this)
this.SetFont('s20')
this.Add('Button', 'vTheBigButton', "Click me").OnEvent('Click', 'Clicked')
this.OnEvent('ContextMenu', 'RightClicked')
}
Clicked(ctrl, *) {
MsgBox ctrl.name " was clicked."
}
RightClicked(ctrl, *) {
MsgBox (ctrl ? ctrl.name : type(this)) " was right-clicked (or AppsKey/Shift+F10 was pressed)."
}
__delete() {
MsgBox type(this) " will be deleted."
}
}
GuiWithButton().Show()
Code: Select all
this.LineLimit := this.Add("Edit", , "9999")
this.Add("Checkbox", "", "Line limit").OnEvent("Click", (*) => this.LineLimit.Enabled := not this.LineLimit.Enabled)
There is a special case in the implementation of __New: when EventObj == this, it avoids calling AddRef or Release, since they aren't needed and would only have a detrimental effect.
Code: Select all
g := Gui()
c := g.AddText()
g := ""
MsgBox type(c.gui) ; Error: The control is destroyed.
Yes. There's no point specifying EventObj if you're not going to use method names for your event handlers. You can also create circular references completely independent of the event handlers, such as by assigning properties. EventObj will obviously not help in those cases.wpb wrote:If I were to add something like this to your example:
..then I'd still be introducing a circular reference with the anonymous function in the same way as you described before, wouldn't I?
The point of the exception is to provide an alternative to the other methods of specifying event handlers which are prone to circular references. You need to actually use that alternative as such, not in combination with the more problematic methods.The Gui retains a reference to EventObj for the purpose of calling event handlers, and releases it when the window is destroyed. If EventObj itself contains a reference to the Gui, this would typically create a circular reference which prevents the Gui from being automatically destroyed. However, an exception is made for when EventObj is the Gui itself, to avoid a circular reference in that case.
I should have been explicit in saying "avoiding function references or boundfunc objects that point to methods in the Gui subclass in event handlers is essential to avoid circular references". Presumably that's an accurate statement, given all we've discussed above?lexikos wrote: ↑25 Mar 2024, 17:05No, avoiding references to the GUI itself being passed indirectly back into the GUI (via an event handler or assigned property) is essential to avoid circular references. You can use bound functions and closures that refer to objects which don't directly or indirectly have any counted reference to the GUI.
Code: Select all
a := SomeClass()
Sleep(10000)
a := ""
class SomeClass
{
__New()
{
SetTimer(this.timedFunc.Bind(this), 1000)
}
__Delete()
{
SetTimer(this.timedFunc.Bind(this), 0)
}
timedFunc()
{
; Blah, blah...
}
}
No, it's actually missing the point. There's no problem with retaining references to methods of the Gui subclass; after all, the object already has indirect references to those via its base object. You could even bind some other Gui instance to a method of your subclass and pass that to OnEvent without creating a reference cycle. The problem is with the Gui object referring indirectly to itself. If you were to bind the Gui object to a global or nested function and pass that to OnEvent, you would create a cycle even though you haven't used a method of the Gui subclass.wpb wrote: I should have been explicit in saying "avoiding function references or boundfunc objects that point to methods in the Gui subclass in event handlers is essential to avoid circular references". Presumably that's an accurate statement, given all we've discussed above?