11local name = ...
22--- @class DialogKeyNS
33local ns = select (2 , ... )
4- _G .DialogKeyNS = ns
4+
5+ local GetMouseFoci = GetMouseFoci or function () return {GetMouseFocus ()} end
6+ local GetFrameMetatable = _G .GetFrameMetatable or function () return getmetatable (CreateFrame (' FRAME' )) end
7+
8+ _G .DialogKeyNS = ns -- expose ourselves to the world :)
59
610--- @class DialogKey : AceAddon , AceEvent-3.0 , AceHook-3.0
711local DialogKey = LibStub (" AceAddon-3.0" ):NewAddon (name , " AceEvent-3.0" , " AceHook-3.0" )
812ns .Core = DialogKey
913
1014local defaultPopupBlacklist = { -- If a confirmation dialog contains one of these strings, don't accept it
11- " Are you sure you want to go back to Shal'Aran?" , -- Withered Training Scenario
12- " Are you sure you want to return to your current timeline?" , -- Leave Chromie Time
13- " You will be removed from Timewalking Campaigns once you use this scroll." , -- "A New Adventure Awaits" Chromie Time scroll
15+ -- "Are you sure you want to go back to Shal'Aran?", -- Withered Training Scenario -- why exclude this?
16+ -- "Are you sure you want to return to your current timeline?", -- Leave Chromie Time -- why exclude this?
17+ -- "You will be removed from Timewalking Campaigns once you use this scroll.", -- "A New Adventure Awaits" Chromie Time scroll -- why exclude this?
1418 AREA_SPIRIT_HEAL , -- Prevents cancelling the resurrection
1519 TOO_MANY_LUA_ERRORS ,
1620 END_BOUND_TRADEABLE ,
1721 ADDON_ACTION_FORBIDDEN ,
1822}
1923
20- local GetMouseFoci = GetMouseFoci or function () return {GetMouseFocus ()} end
21- local GetFrameMetatable = _G .GetFrameMetatable or function () return getmetatable (CreateFrame (' FRAME' )) end
2224local function callFrameMethod (frame , method , ...)
2325 local functionRef = frame [method ] or GetFrameMetatable ().__index [method ] or nop ;
2426 local ok , result = pcall (functionRef , frame , ... );
@@ -27,7 +29,7 @@ local function callFrameMethod(frame, method, ...)
2729end
2830--- @return string ?
2931local function getFrameName (frame )
30- return callFrameMethod (frame , ' GetDebugName' )
32+ return callFrameMethod (frame , ' GetDebugName' ) --- @diagnostic disable-line : return-type-mismatch
3133 or callFrameMethod (frame , ' GetName' )
3234end
3335--- @return Frame ?
@@ -43,6 +45,14 @@ function DialogKey:GetFrameByName(frameName)
4345 return frameTable ;
4446end
4547
48+ DialogKey .playerChoiceButtons = {}
49+ DialogKey .handledCustomFrames = {}
50+ DialogKey .ignoreCheckCustomFrames = false
51+ DialogKey .proxyFrames = {}
52+ DialogKey .proxyFrameNamePrefix = " DialogKeyNumy_ProxyFrame_"
53+ DialogKey .proxyFrameIndex = 0
54+ DialogKey .activeOverrideBindings = {}
55+
4656function DialogKey :OnInitialize ()
4757 if C_AddOns .IsAddOnLoaded (" Immersion" ) then
4858 self :print (" Immersion AddOn detected." )
@@ -55,23 +65,17 @@ function DialogKey:OnInitialize()
5565 if self .db [k ] == nil then self .db [k ] = v end
5666 end
5767
58- self .glowFrame = CreateFrame (" Frame" , nil , UIParent )
59- self .glowFrame :SetPoint (" CENTER" , 0 , 0 )
60- self .glowFrame :SetFrameStrata (" TOOLTIP" )
61- self .glowFrame :SetSize (50 ,50 )
62- self .glowFrame :SetScript (" OnUpdate" , function (...) self :GlowFrameUpdate (... ) end )
63- self .glowFrame :Hide ()
64- self .glowFrame .tex = self .glowFrame :CreateTexture ()
65- self .glowFrame .tex :SetAllPoints ()
66- self .glowFrame .tex :SetColorTexture (1 ,1 ,0 ,0.5 )
68+ self :InitGlowFrame ()
6769
6870 self :RegisterEvent (" GOSSIP_SHOW" )
6971 self :RegisterEvent (" QUEST_GREETING" )
7072 self :RegisterEvent (" QUEST_COMPLETE" )
7173 self :RegisterEvent (" PLAYER_REGEN_DISABLED" )
7274 self :RegisterEvent (" ADDON_LOADED" )
7375
74- self .frame = CreateFrame (" Frame" , " DialogKeyFrame" , UIParent )
76+ self :SecureHook (" CreateFrame" , " CheckCustomFrames" )
77+
78+ self .frame = CreateFrame (" Frame" , nil , UIParent )
7579 self .frame :SetScript (" OnKeyDown" , function (_ , ...) self :HandleKey (... ) end )
7680 self .frame :SetFrameStrata (" TOOLTIP" ) -- Ensure we receive keyboard events first
7781 self .frame :EnableKeyboard (true )
@@ -110,6 +114,7 @@ function DialogKey:ADDON_LOADED(_, addon)
110114 self :SecureHook (PlayerChoiceFrame , " TryShow" , " OnPlayerChoiceShow" )
111115 self :SecureHookScript (PlayerChoiceFrame , " OnHide" , " OnPlayerChoiceHide" )
112116 end
117+ self :CheckCustomFrames ()
113118end
114119
115120function DialogKey :QUEST_COMPLETE ()
@@ -130,7 +135,18 @@ function DialogKey:PLAYER_REGEN_DISABLED()
130135 self :ClearOverrideBindings ()
131136end
132137
133- DialogKey .playerChoiceButtons = {}
138+ function DialogKey :InitGlowFrame ()
139+ self .glowFrame = CreateFrame (" Frame" , nil , UIParent )
140+ self .glowFrame :SetPoint (" CENTER" , 0 , 0 )
141+ self .glowFrame :SetFrameStrata (" TOOLTIP" )
142+ self .glowFrame :SetSize (50 ,50 )
143+ self .glowFrame :SetScript (" OnUpdate" , function (...) self :GlowFrameUpdate (... ) end )
144+ self .glowFrame :Hide ()
145+ self .glowFrame .tex = self .glowFrame :CreateTexture ()
146+ self .glowFrame .tex :SetAllPoints ()
147+ self .glowFrame .tex :SetColorTexture (1 ,1 ,0 ,0.5 )
148+ end
149+
134150function DialogKey :OnPlayerChoiceShow ()
135151 if not self .db .handlePlayerChoice then return end
136152 local frame = PlayerChoiceFrame ;
@@ -226,7 +242,8 @@ function DialogKey:GetFirstVisibleCraftingOrderFrame()
226242 " ProfessionsFrame.OrdersPage.OrderView.CompleteOrderButton" ,
227243 };
228244 for _ , frameName in ipairs (frames ) do
229- local frame = self :GetFrameByName (frameName )
245+ --- @type Button ?
246+ local frame = self :GetFrameByName (frameName ) --- @diagnostic disable-line : assign-type-mismatch
230247 if frame and frame :IsVisible () and self :GuardDisabled (frame ) then
231248 return frame
232249 end
@@ -318,14 +335,15 @@ function DialogKey:GetPopupButton(popupFrame)
318335 return popupFrame .button1
319336end
320337
321- DialogKey .activeOverrideBindings = {}
322338-- Clears all override bindings associated with an owner, clears all override bindings if no owner is passed
339+ --- @param owner Frame ?
323340function DialogKey :ClearOverrideBindings (owner )
324341 if InCombatLockdown () then return end
325342 if not owner then
326343 for owner , _ in pairs (self .activeOverrideBindings ) do
327344 self :ClearOverrideBindings (owner )
328345 end
346+ return
329347 end
330348 if not self .activeOverrideBindings [owner ] then return end
331349 for key in pairs (self .activeOverrideBindings [owner ]) do
336354
337355-- Set an override click binding, these bindings can safely perform secure actions
338356-- Override bindings, are temporary keybinds, which can only be modified out of combat; they are tied to an owner, and need to be cleared when the target is hidden
357+ --- @param owner Frame
358+ --- @param targetName string
359+ --- @param keys string[]
339360function DialogKey :SetOverrideBindings (owner , targetName , keys )
340361 if InCombatLockdown () then return end
341362 self .activeOverrideBindings [owner ] = {}
@@ -345,6 +366,60 @@ function DialogKey:SetOverrideBindings(owner, targetName, keys)
345366 end
346367end
347368
369+ function DialogKey :CheckCustomFrames ()
370+ if self .ignoreCheckCustomFrames then return end
371+ for frameName , _ in pairs (self .db .customFrames ) do
372+ local frame = self :GetFrameByName (frameName )
373+ if frame and not self .handledCustomFrames [frame ] then
374+ self .handledCustomFrames [frame ] = frameName
375+ self :SecureHookScript (frame , " OnShow" , " OnCustomFrameShow" )
376+ self :SecureHookScript (frame , " OnHide" , " OnCustomFrameHide" )
377+ if frame :IsVisible () then
378+ self :OnCustomFrameShow (frame )
379+ end
380+ end
381+ end
382+ end
383+
384+ --- @param frame Frame
385+ --- @return Button , string
386+ function DialogKey :AcquireProxyButton (frame )
387+ local proxyButton = self .proxyFrames [frame ]
388+ if not proxyButton then
389+ self .proxyFrameIndex = self .proxyFrameIndex + 1
390+ local proxyName = self .proxyFrameNamePrefix .. self .proxyFrameIndex
391+ self .ignoreCheckCustomFrames = true
392+ proxyButton = CreateFrame (" Button" , proxyName , nil , " SecureActionButtonTemplate" )
393+ self .ignoreCheckCustomFrames = false
394+ proxyButton :SetAttribute (" type" , " click" )
395+ proxyButton :SetAttribute (" typerelease" , " click" )
396+ proxyButton :SetAttribute (" clickbutton" , frame )
397+ proxyButton :RegisterForClicks (" AnyUp" , " AnyDown" )
398+ proxyButton :SetAttribute (" pressAndHoldAction" , " 1" )
399+ proxyButton :HookScript (" OnClick" , function () self :Glow (frame ) end )
400+ proxyButton .name = proxyName
401+ proxyButton .target = frame
402+ self .proxyFrames [frame ] = proxyButton
403+ end
404+ return proxyButton , proxyButton .name
405+ end
406+
407+ function DialogKey :OnCustomFrameShow (frame )
408+ if InCombatLockdown () or not self .db .customFrames [self .handledCustomFrames [frame ]] then return end
409+
410+ local proxyButton , proxyName = self :AcquireProxyButton (frame )
411+
412+ self :SetOverrideBindings (proxyButton , proxyName , self .db .keys )
413+ end
414+
415+ function DialogKey :OnCustomFrameHide (frame )
416+ if InCombatLockdown () then return end
417+
418+ local proxyButton = self :AcquireProxyButton (frame )
419+
420+ self :ClearOverrideBindings (proxyButton )
421+ end
422+
348423DialogKey .checkOnUpdate = {}
349424--- @param popupFrame StaticPopupTemplate # One of the StaticPopup1-4 frames
350425function DialogKey :OnPopupShow (popupFrame )
@@ -392,8 +467,11 @@ function DialogKey:HandleKey(key)
392467 if doAction then
393468
394469 -- Click Popup - the actual click is performed via OverrideBindings
395- if self :GetFirstVisiblePopup () and self :GetPopupButton (self :GetFirstVisiblePopup ()) then
470+ local popupFrame = self :GetFirstVisiblePopup ()
471+ local popupButton = popupFrame and self :GetPopupButton (popupFrame )
472+ if popupButton then
396473 DialogKey .frame :SetPropagateKeyboardInput (true )
474+ self :Glow (popupButton )
397475 return
398476 end
399477
@@ -409,9 +487,7 @@ function DialogKey:HandleKey(key)
409487 -- Custom Frames
410488 local customFrame = self :GetFirstVisibleCustomFrame ()
411489 if customFrame then
412- DialogKey .frame :SetPropagateKeyboardInput (false )
413- self :Glow (customFrame )
414- customFrame :Click ()
490+ DialogKey .frame :SetPropagateKeyboardInput (true )
415491 return
416492 end
417493
@@ -623,8 +699,11 @@ function DialogKey:AddFrame(frameName)
623699
624700 self .db .customFrames [frameName ] = true
625701 self :Glow (frame , 0.25 , true )
626- self :print (" Added frame: " , frameName , ' . Remove it again with /dialogkey remove; or in the options UI.' )
627- -- todo: consider making it always a secure click
702+ self :print (" Added frame:" , frameName , " . Remove it again with /dialogkey remove; or in the options UI." )
703+ self :CheckCustomFrames ()
704+ if frame :IsVisible () then
705+ self :OnCustomFrameShow (frame )
706+ end
628707end
629708
630709function DialogKey :RemoveFrame (frameName )
@@ -642,8 +721,8 @@ function DialogKey:RemoveFrame(frameName)
642721
643722 self .db .customFrames [frameName ] = nil
644723 self :Glow (frame , 0.25 , true )
645- self :print (" Removed frame: " , frameName )
646- -- todo: if handled by magic secure click code, unregister it
724+ self :print (" Removed frame:" , frameName )
725+ self : OnCustomFrameHide ( frame )
647726end
648727
649728--- Returns the first clickable frame that has mouse focus
0 commit comments