Reply
Thread Tools Display Modes
Unread 03-26-14, 08:39 PM   #1
Duugu
Premium Member
 
Duugu's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 764
Spellflyout, SpellFlyout_Toggle, and taint

Hello all

I'm stuck with a bad problem again.


My intention is to use Blizzards spellflyout feature, and to modify the layout of the flyout itself if the flyout buttons are shown (means: the expanding list of spells).

My first approach was this:

Lua Code:
  1. SecureHandler_OnLoad(_G["SpellFlyoutButton1"])
  2. _G["SpellFlyoutButton1"]:WrapScript(_G["SpellFlyoutButton1"], "OnShow", [[
  3.     local flyoutButtonSize = 29
  4.     local rangeFromAButton = 5
  5.     local t = string.sub(self:GetName(), 18)
  6.     local degrees = self:GetParent():GetParent():GetAttribute("degree") * 3.14159265 / 180
  7.     local nx = math.cos(degrees) - (((flyoutButtonSize * t) + rangeFromAButton) * math.sin(degrees))
  8.     local ny = math.sin(degrees) + (((flyoutButtonSize * t) + rangeFromAButton) * math.cos(degrees))
  9.     if self then
  10.         self:ClearAllPoints()
  11.         self:SetPoint("CENTER", self:GetParent():GetParent(), "CENTER", nx, ny)
  12.     end
  13. ]])

SpellFlyoutButton1 is the first of the expanding flyout buttons, and the solution works very well for the first button. The buttons new position is exactly as expected.

But ... there's a problem with the next buttons (a flyout can have a lot of buttons as you know). If the UI loads then only the first of the buttons (SpellFlyoutButton1) is created. All other buttons are created 'on the fly' if they are really needed.

Here's a part of the code for this from the Blizz interface file SpellFlyout.lua ("Createframe" at the bottom):

Lua Code:
  1. function SpellFlyout_Toggle(self, flyoutID, parent, direction, distance, isActionBar, specID, showFullTooltip)
  2.     if (self:IsShown() and self:GetParent() == parent) then
  3.         self:Hide();
  4.         return;
  5.     end
  6.  
  7.     local offSpec = specID and (specID ~= 0);
  8.    
  9.     -- Save previous parent to update at the end
  10.     local oldParent = self:GetParent();
  11.     local oldIsActionBar = self.isActionBar;
  12.  
  13.     local _, _, numSlots, isKnown = GetFlyoutInfo(flyoutID);
  14.     local actionBar = parent:GetParent();
  15.     self:SetParent(parent);
  16.     self.isActionBar = isActionBar;
  17.  
  18.     -- Make sure this flyout is known or we are showing an offSpec flyout
  19.     if ((not isKnown and not offSpec) or numSlots == 0) then
  20.         self:Hide();
  21.         return;
  22.     end
  23.    
  24.     if (not direction) then
  25.         direction = "UP";
  26.     end
  27.    
  28.     -- Update all spell buttons for this flyout
  29.     local prevButton = nil;
  30.     local numButtons = 0;
  31.     for i=1, numSlots do
  32.         local spellID, overrideSpellID, isKnown, spellName, slotSpecID = GetFlyoutSlotInfo(flyoutID, i);
  33.         local visible = true;
  34.        
  35.         -- Ignore Call Pet spells if there isn't a pet in that slot
  36.         local petIndex, petName = GetCallPetSpellInfo(spellID);
  37.         if (isActionBar and petIndex and (not petName or petName == "")) then
  38.             visible = false;
  39.         end
  40.        
  41.         if ( ((not offSpec or slotSpecID == 0) and visible and isKnown) or (offSpec and slotSpecID == specID) ) then
  42.             local button = _G["SpellFlyoutButton"..numButtons+1];
  43.             if (not button) then
  44.                 button = CreateFrame("CHECKBUTTON", "SpellFlyoutButton"..numButtons+1, SpellFlyout, "SpellFlyoutButtonTemplate");
  45.             end

Now the problem: Obviously I can't set up my WrapScript before the button exists. So I have to listen if the player clicks on a flyout button for the first time (for example via hooking "SpellFlyout_Toggle" - see above) and to apply my script then. Unfortunately this could happen if the player is in combat. And if I then set the WrapScript everything is tainted. :/

The only possible way to work around this I can think of is to force the creation of all flyout buttons before the player enters combat.
My first approach to do this was calling SpellFlyout_Toggle() with a set of suitable arguments to initiate the frame creation. Which worked fine. I've managed it to call the function, the flyout was visible, I've wrapped my script to all of the flyout buttons and everything was great.
Until I entered combat and tried to open the flyout button ... then the interface stated my code was tainted (when executing the wrapped script). :/

That means, if I call SpellFlyout_Toggle() manually from unsecure code, and if the flyout frames are created by SpellFlyout_Toggle() then they are unsecure!? I'm not sure if I do understand why.

The second point is: how do I force the flyout button frames to get build from secure code (like inside a secure snippet) if I can't use SpellFlyout_Toggle() or reference the "Spellflyout" frame there?

I'm a bit lost to be honest. Any thoughts or words that could help to enlighten me are greatly appreciated. Thank you.
Duugu is offline   Reply With Quote
Unread 03-26-14, 09:30 PM   #2
Seerah
Fishing Trainer
 
Seerah's Avatar
WoWInterface Super Mod
Featured
Join Date: Oct 2006
Posts: 9,685
You should modify them as they are created. Don't force them to be created.
__________________
"You'd be surprised how many people violate this simple principle every day of their lives and try to fit square pegs into round holes, ignoring the clear reality that Things Are As They Are." -Benjamin Hoff, The Tao of Pooh

Seerah is offline   Reply With Quote
Unread 03-26-14, 09:31 PM   #3
Duugu
Premium Member
 
Duugu's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 764
Originally Posted by Seerah View Post
You should modify them as they are created. Don't force them to be created.
But if I wait for them to be created it could be in combat. And if I apply the wrapscript in combat the frame is tainted. :/

[e]
Nevermind. Thanks for your reply.

I guess it's less time consuming to build my own flyout feature than trying to use the existing one.

Last edited by Duugu : 03-26-14 at 09:44 PM.
Duugu is offline   Reply With Quote
Unread 03-27-14, 09:12 AM   #4
Duugu
Premium Member
 
Duugu's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 764
The bad thing is, my own flyout would mean I have to call GetFlyoutInfo(). Which means the flyout spells could be dragged to the button out of combat only. :/

The secure framework is such a mess.
Duugu is offline   Reply With Quote
Unread 03-27-14, 01:13 PM   #5
ravagernl
Premium Member
Premium Member
AddOn Author - Click to view addons
Join Date: Feb 2006
Posts: 1,149
Couldn't you store a reference to a secure frame inside an array in combat, and after combat, loop through that same array, do stuff and wipe it after that?
ravagernl is offline   Reply With Quote
Unread 03-27-14, 02:19 PM   #6
Duugu
Premium Member
 
Duugu's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 764
Hm.

To implement my own flyout functionality I'll have to query and store the spells/spellids that a flyout (like the mage 'Portal') provides in when the player drags the flyout to my action button.

If the player then actually opens the flyout I'll have to set those spellids to all the pop out flyout buttons.

The only way to get the actual spellids of a flyout is GetFlyoutInfo()/GetFlyoutSlotInfo(). Which is unsecure and can't be directly called insided a secure snippet. I could call it via CallMethod() and unsecure code. But this will cause taint if I do this in combat.

So as far as I understand the whole thing is, that it is impossible to get the actual spell ids of a flyout in combat without tainting my frames.

Am I wrong?


[e]
I could pre-build a simple list of all flyout spells that are available to get around GetFlyoutInfo. Not smart but at least working.
Does anyone have an idea where to get a list of all available flyout spells?

[e2] Doh. Ok. New idea: I'll query all flyoutIDs up to ... whatever ... 1k and query and store all slots for flyoutIDs that exist on addon load. Then I'll pre-setup flyout action buttons for all of them on addon load. And in combat, if a player drags a flyout to an action button, I'll just have to move/show them. Don't know if this could work, but I'll try it tonight.
Also not smart, but could work.

Last edited by Duugu : 03-27-14 at 02:52 PM.
Duugu is offline   Reply With Quote
Unread 03-27-14, 05:32 PM   #7
Phanx
A Pyroguard Emberseer
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 4,576
Originally Posted by Duugu View Post
New idea: I'll query all flyoutIDs up to ... whatever ... 1k and query and store all slots for flyoutIDs that exist on addon load.
That seems like a lot of unnecessary scanning. Just scan the player's spellbook for the flyouts that are actually available to them.

Lua Code:
  1. local _, _, offset, numSpells = GetSpellTabInfo(2)
  2. for i = offset, numSpells do
  3.     local skillType, skillID = GetSpellBookItemInfo("spell", i)
  4.     if skillType == "FLYOUT" then
  5.         local _, _, numSlots, isKnown = GetFlyoutInfo(skillID)
  6.         for j = 1, numSlots do
  7.             local spellID, overrideSpellID, isKnown, spellName, slotSpecID = GetFlyoutSlotInfo(skillID, j)
  8.         end
  9.     end
  10. end

(I'm pretty sure nobody has any flyouts on the General tab, but if they do, just start the i loop from 1 instead of offset.)
__________________
Author/maintainer of Grid, PhanxChat, ShieldsUp, and many more.
Troubleshoot an addonTurn any code into an addonMore addon resources
Need help with your code? Post all of your actual code! Attach or paste your files.
Please don’t PM me about addon bugs or code questions. Post a comment or forum thread instead!
Phanx is offline   Reply With Quote
Unread 03-27-14, 05:44 PM   #8
Duugu
Premium Member
 
Duugu's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 764
Ok, this is finaly working. If someone has ever to add a flyout functionality to an action button without using type="action" ... this is how it could be done:

Query the players spellbook for all flyout 'spells' if the addon loads (code from Phanx above this post) and store the flyoutid and all the spells for each flyoutid.
Create your secure action button frames.
Create a set of secure action buttons for the Pop up flyout bar (like 10 should be enough).
Use SetFrameRef to add to each of your standard action buttons a reference to all of the 10 Pop up flyout buttons. Like this
Lua Code:
  1. for x = 1, 10 do
  2.         myBtn:SetFrameRef("PopupFlyoutButton"..x, _G[("PopupFlyoutButton"..x])
  3.     end
Add an attribute to all of your action buttons for each of the "flyout sets" you've queried in step one. Like this as an example for flyoutid 11 that contains the spells 1456, 1457 and 1458:
Lua Code:
  1. myBtn:SetAttribute("FlyoutSetInfo11", "1456,1457,1458")

Handle the type "flyout" in the "ReceivingDrag" wrap script for your action buttons as usual and set up a valid texture, etc for your action button. Set the type to "myflyout" and "spell" to the flyoutID (is provided with value or extra value ...).

Now use everything within the "OnClick" wrapscript of your action buttons, and set the flyout pop up buttons. Like this:
Lua Code:
  1. if self:GetAttribute("type") == "myflyout" then
  2.     local x = 1
  3.     for k, _ in string.gmatch(self:GetAttribute("FlyoutSetInfo"..self:GetAttribute("spell")), "(%d+),") do
  4.         local tFlyoutBtn = self:GetFrameRef("PopupFlyoutButton"..x)
  5.         tFlyoutBtn:SetAttribute("type", "spell")
  6.         tFlyoutBtn:SetAttribute("spell", k)
  7.         tFlyoutBtn:RunAttribute("UpdateState", self:GetAttribute("state"))
  8.         --<add your code to update the visual stuff of the flyout pop up button (texture, etc).
  9.         --set the flyout pop up buttons position
  10.         tFlyoutBtn:ClearAllPoints()
  11.         tFlyoutBtn:SetPoint("CENTER", tFlyoutBtn:GetParent(), "CENTER", 0, 50 * x)
  12.         x = x + 1
  13.     end
  14. end

I don't know if this is the only way. Or if it is unreasonably complicated. But this is the only way I could figure out to to this. If you have any improvements or whatever I would love to hear it.

[e]
There's still one point where this flyout is different from the standard flyout: it can be dragged from your button in combat, but it's not possible to set the dragged button to the cursor (the secure codes pickup don't handle "flyout" actions and doing it myself with PickupSpellBookItem would cause taint again). :/

Last edited by Duugu : 03-27-14 at 08:21 PM.
Duugu is offline   Reply With Quote
Unread 03-27-14, 07:23 PM   #9
Clamsoda
A Cobalt Mageweaver
Join Date: Nov 2011
Posts: 245
Originally Posted by Phanx View Post
(I'm pretty sure nobody has any flyouts on the General tab, but if they do, just start the i loop from 1 instead of offset.)
Getting gold in a challenge mode gives you a portal to that instance, stored in a flyout in the general tab called Challenger's Path. Not sure if that information helps.
Clamsoda is offline   Reply With Quote
Unread 03-27-14, 08:12 PM   #10
Duugu
Premium Member
 
Duugu's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 764
Originally Posted by Clamsoda View Post
Getting gold in a challenge mode gives you a portal to that instance, stored in a flyout in the general tab called Challenger's Path. Not sure if that information helps.
Yes, that helps a lot. Thank you for this.
Duugu is offline   Reply With Quote
Reply

Go BackWoWInterface » Developer Discussions » General Authoring Discussion » Spellflyout, SpellFlyout_Toggle, and taint

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off