Torhal, while those perticular scripts may only be used for highlights and beggener tooltips, those perticualr frames are created by blizzard and are secure. Setting a insecure script, IMO, shouse be avoided. A secure hook might be better.
You can't un-hook secure hooks, which is why it was done that way. If this becomes an issue, I have a way of handling it so that it no longer is. :D
I'll just have to finish getting Revelation working without Dewdrop, and see if I have issues with taint or blocked actions from CastSpellByName() and such.
Hi Torhal, your method is too complicated. There is a much simpler method to display tooltips regardless of whether the Beginner Tooltips option is turned on or off.
Here's how.
Step 1: Securehook the function GameTooltip_AddNewbieTip(frame, normalText, r, g, b, newbieText, noNormalText)
[php]
-- Hook the tooltips on UIDropDownMenu
hooksecurefunc("GameTooltip_AddNewbieTip", function(frame, normalText, r, g, b, newbieText, noNormalText)
if normalText == "WoWEquipShowTooltip" then
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
if newbieText > 0 then
GameTooltip:SetHyperlink("item:"..newbieText)
elseif newbieText < 0 then
GameTooltip:SetHyperlink("enchant:"..-newbieText)
end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(L["|cffeda55fCtrl-Click|r to add to/remove from Favorites"], 0, 1, 0)
GameTooltip:AddLine(L["|cffeda55fShift-Click|r to link to chat"], 0, 1, 0)
GameTooltip:Show()
end
end)
[/php]In the above code, what we have done is check if normalText == "WoWEquipShowTooltip". If it is true, I look at the data contained in the newbieText variable and in my case, I chose to make GameTooltip display an itemlink if it is a positive number, and an enchant link if it is a negative number. I added some extra tooltip text after that about Ctrl/Shift clicks.
If you looked in WoWEquip's code, you'll see that the menuitems in question has .keepShownOnClick = true, and the menu is only forcefully closed on a normal click, and kept open on a Shift or Ctrl-click which does different things.
The beauty of this hook is that it really is just about 5 lines of code without all the extra if-then-else logic for checking if the number I passed in is positive or negative. In the simplest use-case, you can just have
[php]
-- Hook the tooltips on UIDropDownMenu
hooksecurefunc("GameTooltip_AddNewbieTip", function(frame, normalText, r, g, b, newbieText, noNormalText)
if normalText == "WoWEquipShowTooltip" then
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
GameTooltip:Clear()
GameTooltip:AddLine(myTooltipTitles[newbieText], 0, 1, 0)
GameTooltip:AddLine(myTooltipTexts[newbieText], 0, 1, 0)
GameTooltip:Show()
end
end)
[/php]where newbieText is just a pointer to the data you wish to display in your tooltips.
Now compare my simple 5 line function hook which is safe, compared to your 60 line behemoth mess of hooks which may or may not have unknown tainting side-effects, you'll probably want to rewrite all of your code. :)
Step 2: In your info[] table when passing to UIDropDownMenu_AddButton(), simply include info.tooltipTitle and info.tooltipText. For example:
[php]
info.func = self.UncheckHack
info.text = v.t
info.value = v
info.icon = GetItemIcon(v[2])
info.tooltipTitle = "WoWEquipShowTooltip"
info.tooltipText = v[2]
UIDropDownMenu_AddButton(info, level)
[/php] info.tooltipTitle and info.tooltipText are passed into GameTooltip_AddNewbieTip() as normalText and newbieText so you only need to match the tooltipTitle to your addon (if more than one addon is using this technique), and use newbieText as pointer to the data you wish to display to in the tooltip.
GameTooltip_AddNewbieTip() is called whenever your mouse enters a UIDropDownMenuButton, regardless of whether newbie tooltips are on or off. Because our securehooked function runs after Blizzard's one, it overwrites the GameTooltip that Blizzard created with normalText and newbieText in it.
I'm not really sure if this is the right place to post this but this guide while teaching me some I'm still lost as to what is happening in this addon.
I took over a bag addon because it was dying out and it was the bag addon I have used since Vanilla and I couldn't continue without it. So I started cleaning it up to be more up to date code wise. The problem I'm coming up with is the Submenus 2 and 3 from the context menu.
When you control right click I get my context menu and it works perfect. When you mouse over the arrow to open the sub menu it shows up as it should. When you move the mouse across the category and into the next sub menu the menu vanishes. It's very buggy if you move the mouse really fast or back and forth sometimes you can get the 2nd and 3rd sub menus to stay visible but in most cases they just vanish and you can't just have to keep moving the mouse back over the arrow to bring then back.
I have read through this tutorial 100% and I can't find anything that would cause this or allow it. So if anyone can point me into a direction as to what I can look for I would really appreciate it. I'm pretty new to coding I dabble in it so I don't know all of it but I am learning more about it.
I can post parts of the code if there is something that would be helpful to determine it.
I'm having some strange issues with UIDropDownMenu.
If there are more than 30 entries in a submenu/ level, it seems that anything above 30 will get grayed out / un-clickable.
Is this something to do with the UIDROPDOWNMENU_MAXBUTTONS taint?
This is a bug Blizzard introduced in the latest patch. They added "self:SetFrameLevel(2)" in their OnLoad script for the menu buttons in a menu in their virtual template. Forcefully setting the menu button to framelevel 2, obviously puts the menu buttons BELOW the menu itself, rendering them unclickable.
The problem only occurs for new buttons that are 2 or more layers deep because each menu level is parented to the menu level just above it, meaning menu level 2 would have a framelevel of 3 if menu level 1 is parented to nothing and has a framelevel of 1. All children of a frame has their relative framelevels adjusted relatively by the same amount when you change the a frame's parent.
The fix looks something like this:
-- To fix Blizzard's bug caused by the new "self:SetFrameLevel(2);"
local function FixFrameLevel(level, ...)
for i = 1, select("#", ...) do
local button = select(i, ...)
button:SetFrameLevel(level)
end
end
function MyAddon.FixMenuFrameLevels()
local f = DropDownList1
local i = 1
while f do
FixFrameLevel(f:GetFrameLevel() + 2, f:GetChildren())
i = i + 1
f = _G["DropDownList"..i]
end
end
-- To fix Blizzard's bug caused by the new "self:SetFrameLevel(2);"
hooksecurefunc("UIDropDownMenu_CreateFrames", MyAddon.FixMenuFrameLevels)
The problem has nothing to do with taint, it is just Blizzard adding code that break things when they should not have. Especially since there is no reason why the framelevel of the button should ever be a hardcoded 2.
It's a rather complicated issue to explain, because on creation of the button, CreateFrame already assigns the parent, and thus assigns the framelevel of the new frame to be 1 higher than its parent. The OnLoad script then runs and changes that. When you mouseover SubMenuA, SubMenuA isn't originally parented to Menu 1, it would have a default framelevel of 1 (the code reparents it then to Menu1, causing it to have a framelevel of 2 and all its children adjusted by +1 relatively), so its framelevel is different from when you mouseover to SubMenuB where it creates more buttons as needed.
The UIDropDownMenu code basically only generates as many buttons and submenu frames as it needs on demand, but once created they remain in memory. If a button was created while the parent it is to be parented to has a framelevel of 2 or less, it would be fine, but anything higher, it screws up.
I'm trying to pass additional data via the Info.value pointer, from what I understand, this means I would have to put the data into a table.
Eg: info.value ={subMenu="someMenu",data=table_a}
The question is, would this be a good idea memory wise, as it seems to create alot of tables that would only be used for a short time.
The current implementation i have is to have info.value in a formatted string where i string.match to pull values out, but I need to add a pointer to a table, which makes the current method invalid. Any insights? Thanks
The string.match method is fine, think of it as data compression, to be uncompressed on use. The user isn't going to open menus very often (it'll be a once in a blue moon event).
If you still need to use a table for a lot of data, just put
info.value = "some key"
where "some key" can be used directly for lookup in a table for the data you need.
Am I crazy, or was there a code snippet posted at some point that showed how to display the default Blizzard unit frame dropdown menus by making a wrapper? I thought it was in the Guides section, but I can't find it anywhere now.
How can I refresh a menu on the fly?
I have a menu button which is a checkbox and has an arrow. In the submenu, I can chose something. When you do, the check has to be checked in the 'parent' button...
How can I refresh a menu on the fly?
I have a menu button which is a checkbox and has an arrow. In the submenu, I can chose something. When you do, the check has to be checked in the 'parent' button...
You can't do so directly. Your 2 options are:
A) Close the "parent" menu by using CloseDropDownMenu(level). So the user is forced to reopen it.
B) Find the name of the current menu button that got clicked on, find its parent one, and show/hide the checkmark manually in code. Think of it as something a bit more advanced than just an "UncheckHack" as suggested above.
If you know you know which frame/button the menu is spawned from then you can close and reopen it (right after another in your code) and it will seam like it refreshed (though this only works for the first menu level)
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
I'll just have to finish getting Revelation working without Dewdrop, and see if I have issues with taint or blocked actions from CastSpellByName() and such.
Here's how.
Step 1: Securehook the function GameTooltip_AddNewbieTip(frame, normalText, r, g, b, newbieText, noNormalText)
[php]
-- Hook the tooltips on UIDropDownMenu
hooksecurefunc("GameTooltip_AddNewbieTip", function(frame, normalText, r, g, b, newbieText, noNormalText)
if normalText == "WoWEquipShowTooltip" then
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
if newbieText > 0 then
GameTooltip:SetHyperlink("item:"..newbieText)
elseif newbieText < 0 then
GameTooltip:SetHyperlink("enchant:"..-newbieText)
end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(L["|cffeda55fCtrl-Click|r to add to/remove from Favorites"], 0, 1, 0)
GameTooltip:AddLine(L["|cffeda55fShift-Click|r to link to chat"], 0, 1, 0)
GameTooltip:Show()
end
end)
[/php]In the above code, what we have done is check if normalText == "WoWEquipShowTooltip". If it is true, I look at the data contained in the newbieText variable and in my case, I chose to make GameTooltip display an itemlink if it is a positive number, and an enchant link if it is a negative number. I added some extra tooltip text after that about Ctrl/Shift clicks.
If you looked in WoWEquip's code, you'll see that the menuitems in question has .keepShownOnClick = true, and the menu is only forcefully closed on a normal click, and kept open on a Shift or Ctrl-click which does different things.
The beauty of this hook is that it really is just about 5 lines of code without all the extra if-then-else logic for checking if the number I passed in is positive or negative. In the simplest use-case, you can just have
[php]
-- Hook the tooltips on UIDropDownMenu
hooksecurefunc("GameTooltip_AddNewbieTip", function(frame, normalText, r, g, b, newbieText, noNormalText)
if normalText == "WoWEquipShowTooltip" then
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
GameTooltip:Clear()
GameTooltip:AddLine(myTooltipTitles[newbieText], 0, 1, 0)
GameTooltip:AddLine(myTooltipTexts[newbieText], 0, 1, 0)
GameTooltip:Show()
end
end)
[/php]where newbieText is just a pointer to the data you wish to display in your tooltips.
Now compare my simple 5 line function hook which is safe, compared to your 60 line behemoth mess of hooks which may or may not have unknown tainting side-effects, you'll probably want to rewrite all of your code. :)
Step 2: In your info[] table when passing to UIDropDownMenu_AddButton(), simply include info.tooltipTitle and info.tooltipText. For example:
[php]
info.func = self.UncheckHack
info.text = v.t
info.value = v
info.icon = GetItemIcon(v[2])
info.tooltipTitle = "WoWEquipShowTooltip"
info.tooltipText = v[2]
UIDropDownMenu_AddButton(info, level)
[/php] info.tooltipTitle and info.tooltipText are passed into GameTooltip_AddNewbieTip() as normalText and newbieText so you only need to match the tooltipTitle to your addon (if more than one addon is using this technique), and use newbieText as pointer to the data you wish to display to in the tooltip.
GameTooltip_AddNewbieTip() is called whenever your mouse enters a UIDropDownMenuButton, regardless of whether newbie tooltips are on or off. Because our securehooked function runs after Blizzard's one, it overwrites the GameTooltip that Blizzard created with normalText and newbieText in it.
I took over a bag addon because it was dying out and it was the bag addon I have used since Vanilla and I couldn't continue without it. So I started cleaning it up to be more up to date code wise. The problem I'm coming up with is the Submenus 2 and 3 from the context menu.
When you control right click I get my context menu and it works perfect. When you mouse over the arrow to open the sub menu it shows up as it should. When you move the mouse across the category and into the next sub menu the menu vanishes. It's very buggy if you move the mouse really fast or back and forth sometimes you can get the 2nd and 3rd sub menus to stay visible but in most cases they just vanish and you can't just have to keep moving the mouse back over the arrow to bring then back.
I have read through this tutorial 100% and I can't find anything that would cause this or allow it. So if anyone can point me into a direction as to what I can look for I would really appreciate it. I'm pretty new to coding I dabble in it so I don't know all of it but I am learning more about it.
I can post parts of the code if there is something that would be helpful to determine it.
(And use that thread.)
popup a tooltip right next tot the dropdown no mater if the newbie tooltips are enabled or disabled
You are correct. However, you still need to hook in the manner I described above if you wish to do non-standard (i.e not 2-liner) tooltips.
If there are more than 30 entries in a submenu/ level, it seems that anything above 30 will get grayed out / un-clickable.
Is this something to do with the UIDROPDOWNMENU_MAXBUTTONS taint?
This is a bug Blizzard introduced in the latest patch. They added "self:SetFrameLevel(2)" in their OnLoad script for the menu buttons in a menu in their virtual template. Forcefully setting the menu button to framelevel 2, obviously puts the menu buttons BELOW the menu itself, rendering them unclickable.
The problem only occurs for new buttons that are 2 or more layers deep because each menu level is parented to the menu level just above it, meaning menu level 2 would have a framelevel of 3 if menu level 1 is parented to nothing and has a framelevel of 1. All children of a frame has their relative framelevels adjusted relatively by the same amount when you change the a frame's parent.
The fix looks something like this:
The problem has nothing to do with taint, it is just Blizzard adding code that break things when they should not have. Especially since there is no reason why the framelevel of the button should ever be a hardcoded 2.
Did you notice this scenario:
Menu 1
-Item1
-Item2
-Item3
-SubMenuA
--Item 1 to 40
-SubMenuB
--Item 1 to 40
If i hard code a loop in menu A and mouse over / populate it, menu b works fine. However if I remove the loop code in menuA, menu B is limited to 30
The UIDropDownMenu code basically only generates as many buttons and submenu frames as it needs on demand, but once created they remain in memory. If a button was created while the parent it is to be parented to has a framelevel of 2 or less, it would be fine, but anything higher, it screws up.
Eg: info.value ={subMenu="someMenu",data=table_a}
The question is, would this be a good idea memory wise, as it seems to create alot of tables that would only be used for a short time.
The current implementation i have is to have info.value in a formatted string where i string.match to pull values out, but I need to add a pointer to a table, which makes the current method invalid. Any insights? Thanks
If you still need to use a table for a lot of data, just put
info.value = "some key"
where "some key" can be used directly for lookup in a table for the data you need.
Anybody remember what I'm talking about?
I have a menu button which is a checkbox and has an arrow. In the submenu, I can chose something. When you do, the check has to be checked in the 'parent' button...
You can't do so directly. Your 2 options are:
A) Close the "parent" menu by using CloseDropDownMenu(level). So the user is forced to reopen it.
B) Find the name of the current menu button that got clicked on, find its parent one, and show/hide the checkmark manually in code. Think of it as something a bit more advanced than just an "UncheckHack" as suggested above.
If you know you know which frame/button the menu is spawned from then you can close and reopen it (right after another in your code) and it will seam like it refreshed (though this only works for the first menu level)