[a105] Closure help

Get help with using AutoHotkey (v2 or newer) and its commands and hotkeys
User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

[a105] Closure help

Post by kczx3 » 25 Sep 2019, 09:39

I have a ContextMenu function that I create a Menu object within using the static keyword. I then have a nested function that is used as the menu item's callback. It references a parameter from the outer scope creating a closure.

It seems that without updating the Menu Item's callback each time the closure only references the first instance of the outer scope variable. Is there a better way to do this so that the nested function uses the new instance of the ctrl parameter each time that the ContextMenu callback is executed?

Code: Select all

copyListRow(ctrl, item, isRightClick, x, y) {
    static rMenu := MenuCreate()
    
    ; seems like I have to update the menu item each time so that the ctrl variable is correct in performCopy
    rMenu.Add("Copy Selected Rows", "performCopy")
    rMenu.Show(x, y)
    
    performCopy(name, pos, menuObj) {
        row := 0
        colCount := ctrl.GetCount("Col")
        while (row := ctrl.GetNext(row)) {
            loop(colCount) {
                str .= ctrl.GetText(row, A_Index) . (A_Index == colCount ? "`n" : "`t")
            }
        }
        
        clipboard := str
    }
}

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a105] Closure help

Post by Helgef » 25 Sep 2019, 11:00

Hi.
If ctrl changes somewhere outside copyListRow and that change needs to be reflected in an instance of performCopy, don't use a free variable, what you describe is how it is supposed to work. You can use a global variable.

I'm not sure I understand exactly what you want to do though.

Cheers.

User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: [a105] Closure help

Post by kczx3 » 25 Sep 2019, 11:13

copyListRow is a callback for the ContextMenu event for several ListViews in a single GUI. ctrl is a reference to the ListView that the ContextMenu was initiated from. I want performCopy to simply execute with the context of ctrl at the time that copyListRow gets executed.

Does that help?

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a105] Closure help

Post by Helgef » 25 Sep 2019, 11:28

think you should create a separate menu for each ListView then, and bind it to each listview's ContextMenu event callback. Then simply show the bound menu in the callback.

Cheers.

Edit, hint,
[spoiler]
[code]
copyListRow(menu, ctrl, item, isRightClick, x, y) {
menu.show x, y
}

myContextMenu(ctrl){
rMenu := MenuCreate()
rMenu.Add("Copy Selected Rows", "performCopy")

return rMenu
performCopy(name, pos, menuObj) {
row := 0
colCount := ctrl.GetCount("Col")
while (row := ctrl.GetNext(row)) {
loop(colCount) {
str .= ctrl.GetText(row, A_Index) . (A_Index == colCount ? "`n" : "`t")
}
}
clipboard := str
}
}

myListView.onevent 'contextmenu', func('copyListRow').bind(myContextMenu(myListView))
[/code]
[/spoiler]

Edit 2, no, just keep your original function but remove static. No need to complicate things with circular references.
Last edited by Helgef on 25 Sep 2019, 11:59, edited 1 time in total.

User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: [a105] Closure help

Post by kczx3 » 25 Sep 2019, 11:57

Awesome, that worked great. Thanks! I was stuck on trying to simply reuse the same menu as it seemed wasteful to make what appear to be essentially copies of the same menu.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a105] Closure help

Post by Helgef » 25 Sep 2019, 12:00

Note that I edited my post.

User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: [a105] Closure help

Post by kczx3 » 25 Sep 2019, 12:14

You don't think it's wasteful to keep recreating the menu?

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [a105] Closure help

Post by Helgef » 25 Sep 2019, 12:25

No :) . Recreating the menu implies it only exists when asked for, so there is less waste of memory space, but potentially a little more cpu work. But either way is minor, so do it in the most convenient way (programatically).

Cheers.

lexikos
Posts: 9688
Joined: 30 Sep 2013, 04:07
Contact:

Re: [a105] Closure help

Post by lexikos » 29 Sep 2019, 00:00

Before Menu objects and nested functions, I used local labels to create subroutines referring to local variables (which was safe because the function was always running when the subroutine was called). I would create the menu and populate it according to local parameters, show it, then delete it. My biggest motivation for implementing Menu objects was that this approach had issues with thread interruption, since the menu names were global.

Specifically, I right click on one item, but my aim is out, so I right click on another item. Then, because the thread is interrupted within or immediately after Menu-Show, the new thread runs before the menu is deleted. So to work around it, the menu had to be deleted at both ends of the function.

Creating the menu on each call and storing it in a local variable (which might then be "enclosed" by a nested function) ensures that you never run into that problem. If you use closures for any menu items, it also ensures that the closures and any values they've captured don't hang around for a long time unnecessarily. It also avoids any issues arising from Add() modifying an item when you're expecting it to create a new one, or adding some items to the end but leaving some at the top because they already existed.

User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: [a105] Closure help

Post by kczx3 » 30 Sep 2019, 07:23

Great, thanks for reinforcing the approach!

Post Reply

Return to “Ask for Help (v2)”