Just going through the code and writing down findings... ... ... Ok now that I got through it, you can skip down to the
bad button
I'll leave the whole journey here, may be useful another day.
The v[1] from tButtons in your iteration is a control created from ZO_MenuBarButtonTemplate1 by default, but different windows override it with their own template (hopefully always derived from the default). The default template's OnInitialized handler calls ZO_MenuBarButtonTemplate_OnInitialized(self). There it creates an instance of class MenuBarButton:
Lua Code:
function ZO_MenuBarButtonTemplate_OnInitialized(self)
self.m_object = MenuBarButton:New(self)
end
function MenuBarButton:Initialize(button)
self.m_button = button
...
end
function MenuBarButton:SetData(owner, buttonData)
self.m_buttonData = buttonData
...
end
So you can get the object from the control, and the control from the object:
Lua Code:
local buttonControl = v[1]
-- this should always be true
assert(buttonControl == buttonControl.m_object.m_button)
m_clickedButton is an instance of MenuBarButton
*** bad button ***
It's quite unfortunate the callback only gets buttonData as the argument. buttonData.control is not coming from the MenuBarButton class, it is set in local function InitializeInventoryFilters(inventory) (ingame/inventory/inventory.lua), i.e. once when the buttons are created. But later, in ZO_InventoryManager:ApplyBackpackLayout, the buttons are released, and some of them acquired from the pool again, but
.control is not re-assigned. And that is the problem - buttonData remains the same, but gets attached to a different button control.
It's a bug in ZO_InventoryManager:ApplyBackpackLayout in the loop containing ZO_MenuBar_AddButton calls. Since MenuBarButton class is only accessible via metatables of existing buttons, the simplest fix will probably be replacing ZO_MenuBar_AddButton:
Lua Code:
function ZO_MenuBar_AddButton(self, buttonData)
local button = self.m_object:AddButton(buttonData)
buttonData.control = button
return button
end