Thread Tools Display Modes
02-06-15, 12:41 PM   #1
MuffinManKen
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 106
Need help with SecureHandlers

I took over maintenance of the addon AutoBar ages ago and there's a bug I have not managed to figure out. For those who don't know AutoBar creates a bar of buttons. When you hover over the buttons a popup of buttons appears. So there is a Hearth button, hovering over it shows a popup of any Hearth objects you have, Mage ports (if you're a mage), etc. All of this works well.

When you move the cursor off of the popup, the popup should close. 99% of the time, it does however when you move the pointer really quickly sometimes it stays open. The original author claimed this was a Blizzard bug where fast movement sometimes failed to fire the leave event. I have no reason to believe he was mistaken, but I also have no proof that it is correct.

His fix was to set an _ontimer snippet to check for the cursor over the frame, and if it had moved, hide the frame. But it doesn't work, and as far as I recall never did.

I believe all of the relevant code is in a single file which is here: http://pastebin.com/8Gn329dh

To help zero in on the issue, the snippet is called "popupNaziSnippet". It's defined on line 163, and attached to the _ontimer attribute on line 564.

I've tried putting print statements in the snippet and even introducing syntax errors, and the lack of feedback makes me believe that the code is just not called. I believe that it is setup to re-trigger itself properly every second to recheck but the initial call is not happening. I'll be honest, while I've made progress on getting my head around this secure stuff, parts of it still feel like black magic to me so I could be completely wrong about a lot of this.

It's a lot of code to look at and it's a bit of a mess, so I appreciate any help you can give me.
  Reply With Quote
02-06-15, 01:05 PM   #2
Duugu
Premium Member
 
Duugu's Avatar
AddOn Author - Click to view addons
Join Date: Nov 2006
Posts: 851
I am just guessing without looking into your code:

As far as I understand your description the layout is like

Code:
--------------
|             |
|    |-----|  |
|    |     |  |
|    |-----|  |
|             |
|    |-----|  |
|    |     |  |
|    |-----|  |
|             |
|    |-----|  |
|    |     |  |
|    |-----|  |
|             |
--------------
where the outer rectangle is the bar and the inner rectangles are the buttons.

I am guessing that the addon assumes that, if the cursor is over a button, it is obligatory to enter/leave the bar frame as the buttons are completely "surrounded" by the bar. Furthermore I'm ssuming that the addon hides the bar if the bar frames OnLeave triggers.

As you wrote this will work if the cursor moves slowly - meaning the cursor moves from the button to the bar to the background (UIParent or whatever the bar is over).

But, as the onenter/onleave events are only validated once per rendered frame it could happen (on very fast cursor movement) that the cursor is moved from the buttons bounds OUT of the bars bounds within a single rendered frame - completly skipping the bar frame. In this case the cursor never is over the bar frame and there are no OnEnter/OnLeave events for the bar frame.

A solution could be to test on each buttons OnLeave if the cursor is over anything except a button or the bar and if this is the case (cursor was moved from a button straight out of the bar bounds) to hide the bar.

Last edited by Duugu : 02-06-15 at 01:16 PM.
  Reply With Quote
02-08-15, 08:27 PM   #3
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
As I see, there is no '_ontimer' script handler, I don't think it would work. And there is no need to check the "OnLeave".

Take some code from your code
Lua Code:
  1. SecureHandlerWrapScript(frame, "OnEnter", popupHandler, [[
  2.                                         popupHeader = self:GetFrameRef("popupHeader")
  3.                                         popupHeader:Show()
  4.                                         print("OnEnter")
  5.                                 ]])

So, when your mouse enter the frame, show the popupHeader, and when mouse moved away from the popupHeader and the frame, hide the popupHeader. You can use the SecureHoverDriver to do the dirty work.

Lua Code:
  1. SecureHandlerWrapScript(frame, "OnEnter", popupHandler, [[
  2.             popupHeader = self:GetFrameRef("popupHeader")
  3.             popupHeader:Show()
  4.  
  5.             popupHeader:RegisterAutoHide(2)  -- Register auto hide
  6.             popupHeader:AddToAutoHide(self)  -- Add the frame to the check area
  7.     ]])

The SecureHoverDriver would check the popupHeader:GetRect() and frame:GetRect(), so no matter the mouse is on the popupHeader or it's buttons.
  Reply With Quote
02-09-15, 05:00 PM   #4
MuffinManKen
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 106
Originally Posted by Duugu View Post
As far as I understand your description the layout is like
*snip*
where the outer rectangle is the bar and the inner rectangles are the buttons.
It looks like that's not the case. The "bar" doesn't not appear to enclose the buttons, they are just attached at one end so this doesn't seem like it's the issue.
  Reply With Quote
02-09-15, 05:05 PM   #5
MuffinManKen
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 106
Originally Posted by kurapica.igas View Post
The SecureHoverDriver would check the popupHeader:GetRect() and frame:GetRect(), so no matter the mouse is on the popupHeader or it's buttons.
I tried this out quickly and it appears to work at first, but since it appears that the buttons stretch off into space outside of their parent frame, it doesn't track them properly.

So as a quick hack, I tried calling GetChildren to add them to the tracking as well, but that doesn't work; I get an error about directly creating a table in the restricted environment.
  Reply With Quote
02-09-15, 05:30 PM   #6
Banknorris
A Chromatic Dragonspawn
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 153
As far as I know you can create a table in the restricted enviroment with newtable():
a = newtable()
  Reply With Quote
02-09-15, 05:44 PM   #7
MuffinManKen
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 106
Originally Posted by Banknorris View Post
As far as I know you can create a table in the restricted enviroment with newtable():
a = newtable()
Yeah, I just figured that out.

Unfortunately, it doesn't help. I am able to iterate through the child buttons and add them to be watched, but if I move the mouse really fast, the popup is still stuck open.
  Reply With Quote
02-09-15, 08:03 PM   #8
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
The SecureHoverDriver will check whether the mouse is in the area by the time, so if the OnEnter wrapper is called, move mouse away, with a little sec, it'd be closed. It doesn't matter how quick you move the mouse away.
  Reply With Quote
02-09-15, 08:27 PM   #9
MuffinManKen
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 106
Originally Posted by kurapica.igas View Post
The SecureHoverDriver will check whether the mouse is in the area by the time, so if the OnEnter wrapper is called, move mouse away, with a little sec, it'd be closed. It doesn't matter how quick you move the mouse away.
I don't know what to tell you. I changed that snippet to be this:
Lua Code:
  1. SecureHandlerWrapScript(frame, "OnEnter", popupHandler, [[
  2.                     popupHeader = self:GetFrameRef("popupHeader")
  3.                     popupHeader:Show()
  4.                     popupHeader:RegisterAutoHide(0.25)  -- Register auto hide                        
  5.                     popupHeader:AddToAutoHide(self)  -- Add the frame to the check area
  6.                                        
  7.                     local children = table.new()
  8.                     popupHeader:GetChildList(children)
  9.                     for i=1, #children do
  10.                         popupHeader:AddToAutoHide(children[i]) -- Add children to the check area
  11.                     end
  12.                    
  13.             ]])

And I'm able to get the popups to stick open when moving quickly. Maybe there's something else that's interfering with the proper behaviour?
  Reply With Quote
02-09-15, 08:48 PM   #10
Banknorris
A Chromatic Dragonspawn
 
Banknorris's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2014
Posts: 153
Quite sure you need to assign the hover stuff inside the OnShow not inside the OnEnter. In the OnEnter you tell the frame to show but you can only set the hover after is is being shown. And will need to heritage from "SecureHandlerShowHideTemplate"

Last edited by Banknorris : 02-09-15 at 09:01 PM.
  Reply With Quote
02-10-15, 06:01 AM   #11
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
I think I know why, I faced the problem before in my IGAS_UI.

When one frame is RegisterAutoHide, the SecureHoverDriver would first check if the mouse is in it, and if not, it'll wait the mouse move in and be checked, then start the time counting.

So, add the popupHeader to auto hide, but your mouse never move into it, would cause the popupHead can't be hidden.

I use a little trick on it, for your example :

Lua Code:
  1. SecureHandlerWrapScript(frame, "OnEnter", popupHandler, [[
  2.         popupHeader = self:GetFrameRef("popupHeader")
  3.         popupHeader:Show()
  4.  
  5.         self:RegisterAutoHide(0.25)     -- Register auto hide for frame, since the mouse is in it now
  6.  
  7.         self:AddToAutoHide(popupHeader)  -- Add the popupHeader to the check area
  8.                            
  9.         -- Maybe not needed, I don't know what layout you are using
  10.         local children = table.new()
  11.         popupHeader:GetChildList(children)
  12.         for i=1, #children do
  13.             self:AddToAutoHide(children[i]) -- Add children to the check area
  14.         end
  15.        
  16. ]])

Since the mouse is in the frame, just auto hide the frame, the SecureHoverDriver'd handle it very well.

Now, it's the trick, when the SecureHoverDriver hide the frame, it'd change the 'statehidden' attribute to true, then we can handle it like :

Lua Code:
  1. SecureHandlerWrapScript(frame, "OnAttributeChanged", popupHandler, [[
  2.     if name == "statehidden" then
  3.         if value then
  4.             popupHeader = self:GetFrameRef("popupHeader")
  5.             popupHeader:Hide()
  6.  
  7.             self:Show()
  8.         end
  9.     end
  10. ]])

When the frame is hide, just show it again and hide the popupHeader.

The code would cause the frame can't be hidden, just use some bool flag to control it since they are all running in the popupHandler's env.

Last edited by kurapica.igas : 02-10-15 at 06:18 AM.
  Reply With Quote
02-16-15, 02:57 AM   #12
MuffinManKen
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 106
Here is the best solution so far from kurapica.igas. It's a big improvement over what I had, so I'm posting it in case it helps anyone else. It's not perfect though. After a fair bit of testing I was able to get the popups to still stick open. I'm wondering if it's possible to get a perfect solution.




When the button's OnEnter is fired, the SecureHoverDriver OnUpdate is fired the same time, since the button and it's popupHeader is registered, the SecureHoverDriver know it's time to start watching.

Here's is the full solution :

1. Under the
Lua Code:
  1. local popupHandler = CreateFrame("Frame", "AutoBarPopupHandler", nil, "SecureHandlerEnterLeaveTemplate")

Add

Lua Code:
  1. SecureHandlerExecute(popupHandler, [[
  2.     Manager = self -- keep self to block the blz's bug away
  3.  
  4.     PopupMap = newtable()
  5.  
  6.     StartAutoHide = [=[
  7.         if self:GetRect() then   -- the driver need GetRect information
  8.             local popupHeader = self:GetFrameRef("popupHeader")
  9.             self:UnregisterAutoHide()
  10.             self:RegisterAutoHide(0.25)
  11.             self:AddToAutoHide(popupHeader)
  12.         end
  13.     ]=]
  14. ]])
2. Here is the part to replace your OnEnter and OnLeave wrap :

Lua Code:
  1. -- Start auto hide workflow when the button is shown
  2. SecureHandlerWrapScript(frame, "OnShow", popupHandler, [[
  3.     Manager:RunFor(self, StartAutoHide)
  4. ]])
  5.  
  6. SecureHandlerWrapScript(frame, "OnEnter", popupHandler, [[
  7.     self:GetFrameRef("popupHeader"):Show()
  8.     PopupMap[self] = true
  9. ]])
  10. popupHandler.TooltipHide = AutoBar.Class.BasicButton.TooltipHide
  11. SecureHandlerWrapScript(frame, "OnLeave", popupHandler, [[
  12.     self:GetFrameRef("popupHeader"):Hide()
  13.     PopupMap[self] = nil
  14.  
  15.     Manager:RunFor(self, StartAutoHide)
  16. ]])
  17. popupHandler:SetAttribute("_adopt", frame)
  18.  
  19. SecureHandlerWrapScript(frame, "OnAttributeChanged", popupHandler, [[
  20.     if name == "statehidden" then
  21.         if value and PopupMap[self] then
  22.             self:GetFrameRef("popupHeader"):Hide()
  23.             PopupMap[self] = nil
  24.             self:Show()
  25.         elseif value then
  26.             self:UnregisterAutoHide()
  27.         end
  28.     end
  29. ]])
Here is the explain

1. start auto hide when the frame is shown, so the SecureHoverDriver can watch it.

3. mouse enter the frame, trigger the popupHeader show, can use popupMap to track it, so we know when the frame is hidden, it's used to hide the popupHeader. So when you hide the frame directly, the popupMap won't block you.

4. mouse move away the frame and the event is triggered, so just hide the popupHeader, and start whole a new auto hide workflow.

5. If the SecureHoverDriver hide the frame and the popupMap is true, hide the popupHeader and show the frame, with the OnShow wrap script, the new auto hide workflow will started.

So, this is the final solution, but be aware, since the frames are always be registered to the SecureHoverDriver, the driver would check them for every OnUpdate, not much. Just test it by yourself.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Need help with SecureHandlers


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