From bd1abc70d2567f625f4a22e91365fb3133fddd5c Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sat, 18 Mar 2023 11:31:24 -0700 Subject: [PATCH 01/23] Create README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..97b5fde --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# FloFlyout +wow addon to provide custom flyout menus - created by Boboseb From 856e0ce85e6c8d006e3cd0aacd6a63f6bda6ae72 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sat, 18 Mar 2023 21:42:24 -0700 Subject: [PATCH 02/23] fix for issue #1 - errors when empty flyouts exist on bars --- FloFlyout.lua | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/FloFlyout.lua b/FloFlyout.lua index 79f1bda..77f007f 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -70,6 +70,10 @@ local L = FLOFLYOUT_L10N_STRINGS -- Functions ------------------------------------------------------------------------------- +function isEmpty(s) + return s == nil or s == '' +end + function FloFlyout.ReadCmd(line) local cmd, arg1, arg2 = strsplit(' ', line or "", 3); @@ -228,7 +232,7 @@ function FloFlyoutFrame_OnEvent(self, event, ...) if event == "SPELL_UPDATE_COOLDOWN" then local i = 1 local button = _G[self:GetName().."Button"..i] - while (button and button:IsShown() and button.spellID) do + while (button and button:IsShown() and not isEmpty(button.spellID)) do SpellFlyoutButton_UpdateCooldown(button) i = i+1 button = _G[self:GetName().."Button"..i] @@ -437,11 +441,11 @@ local function Opener_PreClick(self, button, down) local buttonList = { FloFlyoutFrame:GetChildren() } table.remove(buttonList, 1) for i, buttonRef in ipairs(buttonList) do - if spellList[i] then - buttonRef.spellID = spellList[i] - buttonRef.actionType = typeList[i] - local icon = FloFlyout:GetTexture(typeList[i], spellList[i]) - _G[buttonRef:GetName().."Icon"]:SetTexture(icon) + buttonRef.spellID = spellList[i] + buttonRef.actionType = typeList[i] + local icon = FloFlyout:GetTexture(typeList[i], spellList[i]) + _G[buttonRef:GetName().."Icon"]:SetTexture(icon) + if true or not isEmpty(spellList[i]) then SpellFlyoutButton_UpdateCooldown(buttonRef) SpellFlyoutButton_UpdateState(buttonRef) SpellFlyoutButton_UpdateUsable(buttonRef) @@ -588,15 +592,17 @@ function FloFlyout:CreateOpener(name, idFlyout, actionId, direction, actionButto _classicUI.LayoutActionButton(opener, typeActionButton) opener:SetScale(actionButton:GetScale()) end - if actionButton:GetSize() and actionButton:IsRectValid() then - opener:SetAllPoints(actionButton) - else - local spacerName = "ActionBarButtonSpacer"..tostring(actionButton.index) - local children = {actionButton:GetParent():GetChildren()} - for _, child in ipairs(children) do - if child:GetName() == spacerName then - opener:SetAllPoints(child) - break; + if actionButton then + if actionButton:GetSize() and actionButton:IsRectValid() then + opener:SetAllPoints(actionButton) + else + local spacerName = "ActionBarButtonSpacer"..tostring(actionButton.index) + local children = {actionButton:GetParent():GetChildren()} + for _, child in ipairs(children) do + if child:GetName() == spacerName then + opener:SetAllPoints(child) + break; + end end end end From b017a22c0a9b948ab984fcc512de407b8f34af0d Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sat, 18 Mar 2023 22:06:17 -0700 Subject: [PATCH 03/23] FIX for fix for issue #1 - oops, left in a debug clause --- FloFlyout.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FloFlyout.lua b/FloFlyout.lua index 77f007f..8150f74 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -445,7 +445,7 @@ local function Opener_PreClick(self, button, down) buttonRef.actionType = typeList[i] local icon = FloFlyout:GetTexture(typeList[i], spellList[i]) _G[buttonRef:GetName().."Icon"]:SetTexture(icon) - if true or not isEmpty(spellList[i]) then + if not isEmpty(spellList[i]) then SpellFlyoutButton_UpdateCooldown(buttonRef) SpellFlyoutButton_UpdateState(buttonRef) SpellFlyoutButton_UpdateUsable(buttonRef) From 75498850ac3d81eea0c72a38c381e912128452a1 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sun, 19 Mar 2023 17:45:19 -0700 Subject: [PATCH 04/23] fix for issue #3 - icon selection panel throws error when scrolling --- FloFlyout.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FloFlyout.xml b/FloFlyout.xml index e86e243..0841aa2 100755 --- a/FloFlyout.xml +++ b/FloFlyout.xml @@ -463,7 +463,7 @@ - FauxScrollFrame_OnVerticalScroll(self, offset, GEARSET_ICON_ROW_HEIGHT, FloFlyoutConfigDialogPopup_Update); + FauxScrollFrame_OnVerticalScroll(self, offset, 36, FloFlyoutConfigDialogPopup_Update); From b11264f21ff2ea14e2bce7567ada7336ccc206e0 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Tue, 4 Apr 2023 10:22:29 -0700 Subject: [PATCH 05/23] fix for issue #5 - flyouts don't turn on/off when the user turns its action bar on/off --- FloFlyout.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/FloFlyout.lua b/FloFlyout.lua index 79f1bda..d019a76 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -128,6 +128,8 @@ function FloFlyout_OnLoad(self) self:RegisterEvent("UPDATE_BINDINGS") self:RegisterEvent("ACTIONBAR_SLOT_CHANGED") + EventRegistry:RegisterCallback("ActionBarShownSettingUpdated", function() FloFlyout:ApplyConfig() end , FloFlyout); + _classicUI = _G["ClassicUI"] if _classicUI then DEFAULT_CHAT_FRAME:AddMessage( NAME.." : |cffd78900ClassicUI v".._classicUI.VERSION.."|r detected." ) From 21aadd63a4d8d8dda741ce935702c1f71d5a4cb2 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Fri, 7 Apr 2023 17:54:10 -0700 Subject: [PATCH 06/23] fix for issue #7 - openers on the primary action bar vanish --- FloFlyout.lua | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/FloFlyout.lua b/FloFlyout.lua index d019a76..c360c04 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -127,6 +127,7 @@ function FloFlyout_OnLoad(self) self:RegisterEvent("PLAYER_ALIVE") self:RegisterEvent("UPDATE_BINDINGS") self:RegisterEvent("ACTIONBAR_SLOT_CHANGED") + self:RegisterEvent("CURSOR_CHANGED") EventRegistry:RegisterCallback("ActionBarShownSettingUpdated", function() FloFlyout:ApplyConfig() end , FloFlyout); @@ -221,11 +222,47 @@ function FloFlyout_OnEvent(self, event, arg1, ...) elseif event == "PLAYER_SPECIALIZATION_CHANGED" then FloFlyout:ApplyConfig() + + elseif event == "CURSOR_CHANGED" then + FloFlyout:FixOpenerStrata(arg1, ...) else end end +function FloFlyout:FixOpenerStrata(isDefault, ...) + --local newCursorType, oldCursorType = ... + --print("-*-FixOpenerStrata-*- ... isDefault =",isDefault, "newCursorType =", newCursorType, "oldCursorType =", oldCursorType, "opener = ",opener) + if isDefault then + self:NormalizeOpenerStrata() + else + self:ElevateOpenerStrata() + end +end + +function FloFlyout:ElevateOpenerStrata() + self:ApplyOperationToAllOpenerInstances(function(opener) + opener:SetFrameStrata("TOOLTIP") + end) +end + +function FloFlyout:NormalizeOpenerStrata() + self:ApplyOperationToAllOpenerInstances(function(opener) + opener:SetFrameStrata("MEDIUM") + end) +end + +function FloFlyout:ApplyOperationToAllOpenerInstances(callback) + for btnName, openerObj in pairs(self.openers) do + callback(openerObj, btnName) + end +end + +function FloFlyout:ApplyOperationToAllOpenerInstancesUnlessInCombat(callback) + if InCombatLockdown() then return end + self:ApplyOperationToAllOpenerInstances(callback) +end + function FloFlyoutFrame_OnEvent(self, event, ...) if event == "SPELL_UPDATE_COOLDOWN" then local i = 1 From a90dd81d95abb752acf018cc60aaf55cf83db15e Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Fri, 7 Apr 2023 19:23:21 -0700 Subject: [PATCH 07/23] fix for issue #7 - openers on the primary action bar vanish - addendum: oops, didn't fix the case when a new opener is created and dropped onto an already occupied btn slot --- FloFlyout.lua | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/FloFlyout.lua b/FloFlyout.lua index c360c04..4dd139c 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -230,9 +230,13 @@ function FloFlyout_OnEvent(self, event, arg1, ...) end end +local STRATA_DEFAULT = "MEDIUM" +local STRATA_MAX = "TOOLTIP" + function FloFlyout:FixOpenerStrata(isDefault, ...) --local newCursorType, oldCursorType = ... --print("-*-FixOpenerStrata-*- ... isDefault =",isDefault, "newCursorType =", newCursorType, "oldCursorType =", oldCursorType, "opener = ",opener) + -- KNOWN BUG: the Bliz CURSOR_CHANGED event lies when you drop a spell on another spell: it reports isDefault==true which is wrong if isDefault then self:NormalizeOpenerStrata() else @@ -242,13 +246,13 @@ end function FloFlyout:ElevateOpenerStrata() self:ApplyOperationToAllOpenerInstances(function(opener) - opener:SetFrameStrata("TOOLTIP") + opener:SetFrameStrata(STRATA_MAX) end) end function FloFlyout:NormalizeOpenerStrata() self:ApplyOperationToAllOpenerInstances(function(opener) - opener:SetFrameStrata("MEDIUM") + opener:SetFrameStrata(STRATA_DEFAULT) end) end @@ -639,7 +643,16 @@ function FloFlyout:CreateOpener(name, idFlyout, actionId, direction, actionButto end end end - opener:SetFrameStrata("MEDIUM") + + local strata + local thingOnCursor = GetCursorInfo() + if thingOnCursor then + strata = STRATA_MAX + else + strata = STRATA_DEFAULT + end + + opener:SetFrameStrata(strata) opener:SetFrameLevel(100) opener:SetToplevel(true) From d835a342c799ec9b95ed2df12b3f12f0dec4cbe6 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sun, 9 Apr 2023 10:46:32 -0700 Subject: [PATCH 08/23] re-fix for issue #7 and issue #9 - openers on the primary action bar vanish - REVAMP! Found a much better way involving setting the opener Frame's parent. Also, shrank the code a lot. For issue #9, I ask the bar object itself for the correct direction. --- FloFlyout.lua | 203 ++++++++++++++++---------------------------------- 1 file changed, 64 insertions(+), 139 deletions(-) diff --git a/FloFlyout.lua b/FloFlyout.lua index 4dd139c..72e5ad7 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -1,6 +1,8 @@ -- This Source Code Form is subject to the terms of the Mozilla Public -- License, v. 2.0. If a copy of the MPL was not distributed with this file, -- You can obtain one at http://mozilla.org/MPL/2.0/. +local MY_NAME, MY_GLOBALS = ... +local DEBUG = MY_GLOBALS.DEBUG -- my debugging routines that I don't check-in ------------------------------------------------------------------------------- -- Constants @@ -12,6 +14,25 @@ local SPELLFLYOUT_DEFAULT_SPACING = 4 local SPELLFLYOUT_INITIAL_SPACING = 7 local SPELLFLYOUT_FINAL_SPACING = 4 local STRIPE_COLOR = {r=0.9, g=0.9, b=1} +local STRATA_DEFAULT = "MEDIUM" +local STRATA_MAX = "TOOLTIP" +local BLIZ_BAR_METADATA = { + [1] = {name="Action", visibleIf="bar:1,nobonusbar:1,nobonusbar:2,nobonusbar:3,nobonusbar:4"}, -- primary "ActionBar" - page #1 - no stance/shapeshift --- ff: actionBarPage = 1 + [2] = {name="Action", visibleIf="bar:2"}, -- primary "ActionBar" - page #2 (regardless of stance/shapeshift) --- ff: actionBarPage = 2 + [3] = {name="MultiBarRight", classicType=2}, -- config UI -> Action Bars -> checkbox #4 + [4] = {name="MultiBarLeft", classicType=2}, -- config UI -> Action Bars -> checkbox #5 + [5] = {name="MultiBarBottomRight", classicType=1}, -- config UI -> Action Bars -> checkbox #3 + [6] = {name="MultiBarBottomLeft", classicType=1}, -- config UI -> Action Bars -> checkbox #2 + [7] = {name="Action", visibleIf="bar:1,bonusbar:1"}, -- primary "ActionBar" - page #1 - bonusbar 1 - druid CAT + [8] = {name="Action", visibleIf="bar:1,bonusbar:2"}, -- primary "ActionBar" - page #1 - bonusbar 2 - unknown? + [9] = {name="Action", visibleIf="bar:1,bonusbar:3"}, -- primary "ActionBar" - page #1 - bonusbar 3 - druid BEAR + [10] = {name="Action", visibleIf="bar:1,bonusbar:4"}, -- primary "ActionBar" - page #1 - bonusbar 4 - druid MOONKIN + [11] = {name="Action", visibleIf="bar:1,bonusbar:5"}, -- primary "ActionBar" - page #1 - bonusbar 5 - dragon riding + [12] = {name="Action", visibleIf="bar:1,bonusbar:6"--[[just a guess]]}, -- unknown? + [13] = {name="MultiBar5"}, -- config UI -> Action Bars -> checkbox #6 + [14] = {name="MultiBar6"}, -- config UI -> Action Bars -> checkbox #7 + [15] = {name="MultiBar7"}, -- config UI -> Action Bars -> checkbox #8 +} ------------------------------------------------------------------------------- -- Variables @@ -127,9 +148,6 @@ function FloFlyout_OnLoad(self) self:RegisterEvent("PLAYER_ALIVE") self:RegisterEvent("UPDATE_BINDINGS") self:RegisterEvent("ACTIONBAR_SLOT_CHANGED") - self:RegisterEvent("CURSOR_CHANGED") - - EventRegistry:RegisterCallback("ActionBarShownSettingUpdated", function() FloFlyout:ApplyConfig() end , FloFlyout); _classicUI = _G["ClassicUI"] if _classicUI then @@ -151,7 +169,7 @@ function FloFlyout_OnEvent(self, event, arg1, ...) FloFlyout:ApplyConfig() end - --elseif event == "SPELL_UPDATE_COOLDOWN" or event == "ACTIONBAR_UPDATE_USABLE" then + --elseif event == "SPELL_UPDATE_COOLDOWN" or event == "ACTIONBAR_UPDATE_USABLE" then elseif event == "ADDON_LOADED" and arg1 == NAME then @@ -166,7 +184,7 @@ function FloFlyout_OnEvent(self, event, arg1, ...) end FloFlyoutConfigFlyoutFrame.IsConfig = true - + for _, flyout in ipairs(FloFlyout.config.flyouts) do if flyout.actionTypes == nil then flyout.actionTypes = {} @@ -178,7 +196,7 @@ function FloFlyout_OnEvent(self, event, arg1, ...) flyout.mountIndex = {} end if flyout.spellNames == nil then - flyout.spellNames = {} + flyout.spellNames = {} end end @@ -223,42 +241,8 @@ function FloFlyout_OnEvent(self, event, arg1, ...) elseif event == "PLAYER_SPECIALIZATION_CHANGED" then FloFlyout:ApplyConfig() - elseif event == "CURSOR_CHANGED" then - FloFlyout:FixOpenerStrata(arg1, ...) - else - - end -end - -local STRATA_DEFAULT = "MEDIUM" -local STRATA_MAX = "TOOLTIP" - -function FloFlyout:FixOpenerStrata(isDefault, ...) - --local newCursorType, oldCursorType = ... - --print("-*-FixOpenerStrata-*- ... isDefault =",isDefault, "newCursorType =", newCursorType, "oldCursorType =", oldCursorType, "opener = ",opener) - -- KNOWN BUG: the Bliz CURSOR_CHANGED event lies when you drop a spell on another spell: it reports isDefault==true which is wrong - if isDefault then - self:NormalizeOpenerStrata() else - self:ElevateOpenerStrata() - end -end -function FloFlyout:ElevateOpenerStrata() - self:ApplyOperationToAllOpenerInstances(function(opener) - opener:SetFrameStrata(STRATA_MAX) - end) -end - -function FloFlyout:NormalizeOpenerStrata() - self:ApplyOperationToAllOpenerInstances(function(opener) - opener:SetFrameStrata(STRATA_DEFAULT) - end) -end - -function FloFlyout:ApplyOperationToAllOpenerInstances(callback) - for btnName, openerObj in pairs(self.openers) do - callback(openerObj, btnName) end end @@ -332,72 +316,29 @@ function FloFlyout:GetName(actionType, data) end end -function FloFlyout:BindFlyoutToAction(idFlyout, idAction) - - local direction, actionButton, actionBarPage, bonusBar, typeActionButton - typeActionButton = 0 - direction = "UP" - - if idAction <= 12 then - bonusBar = 0 - actionBarPage = 1 - actionButton = _G["ActionButton"..idAction] - elseif idAction <= 24 then - actionBarPage = 2 - actionButton = _G["ActionButton"..(idAction - 12)] - elseif idAction <= 36 then - if MultiBar3_IsVisible() then - actionButton = _G["MultiBarRightButton"..(idAction - 24)] - direction = "LEFT" - typeActionButton = 2 - else - actionBarPage = 3 - actionButton = _G["ActionButton"..(idAction - 24)] - end - elseif idAction <= 48 then - if MultiBar4_IsVisible() then - actionButton = _G["MultiBarLeftButton"..(idAction - 36)] - direction = "LEFT" - typeActionButton = 2 - else - actionBarPage = 4 - actionButton = _G["ActionButton"..(idAction - 36)] - end - elseif idAction <= 60 then - if MultiBar2_IsVisible() then - actionButton = _G["MultiBarBottomRightButton"..(idAction - 48)] - typeActionButton = 1 - else - actionBarPage = 5 - actionButton = _G["ActionButton"..(idAction - 48)] - end - elseif idAction <= 72 then - if MultiBar1_IsVisible() then - actionButton = _G["MultiBarBottomLeftButton"..(idAction - 60)] - typeActionButton = 1 - else - actionBarPage = 6 - actionButton = _G["ActionButton"..(idAction - 60)] - end - elseif idAction <= 84 then - bonusBar = 1 - actionBarPage = 1 - actionButton = _G["ActionButton"..(idAction - 72)] - elseif idAction <= 96 then - bonusBar = 2 - actionBarPage = 1 - actionButton = _G["ActionButton"..(idAction - 84)] - elseif idAction <= 108 then - bonusBar = 3 - actionBarPage = 1 - actionButton = _G["ActionButton"..(idAction - 96)] - elseif idAction <= 120 then - bonusBar = 4 - actionBarPage = 1 - actionButton = _G["ActionButton"..(idAction - 108)] - end - - FloFlyout:CreateOpener("FloFlyoutOpener"..idAction, idFlyout, idAction, direction, actionButton, actionBarPage, bonusBar, typeActionButton) +function FloFlyout:BindFlyoutToAction(ffUniqueId, slotIndex) + -- examine the action/bonus/multi bar + local barNum = ActionButtonUtil.GetPageForSlot(slotIndex) + local blizBarDef = BLIZ_BAR_METADATA[barNum] + assert(blizBarDef, "No "..MY_NAME.." config defined for button bar #"..barNum) -- in case Blizzard adds more bars, complain here clearly. + local blizBarName = blizBarDef.name + local visibleIf = blizBarDef.visibleIf + local typeActionButton = blizBarDef.classicType -- for WoW classic + + -- examine the button + local btnNum = (slotIndex % NUM_ACTIONBAR_BUTTONS) -- defined in bliz internals ActionButtonUtil.lua + if (btnNum == 0) then btnNum = NUM_ACTIONBAR_BUTTONS end -- button #12 divided by 12 is 1 remainder 0. Thus, treat a 0 as a 12 + local btnName = blizBarName .. "Button" .. btnNum + local btnObj = _G[btnName] -- grab the button object from Blizzard's GLOBAL dumping ground + + -- ask the bar instance what direction to fly + local barObj = btnObj and btnObj.bar + local direction = barObj and barObj:GetSpellFlyoutDirection() or "UP" -- TODO: fix bug where edit-mode -> change direction doesn't automatically update existing openers + + --local foo = btnObj and "FOUND" or "NiL" + --print ("###--->>> ffUniqueId =", ffUniqueId, "barNum =",barNum, "slotId = ", slotIndex, "btnObj =",foo, "blizBarName = ",blizBarName, "btnName =",btnName, "btnNum =",btnNum, "direction =",direction, "visibleIf =", visibleIf) + + FloFlyout:CreateOpener(slotIndex, ffUniqueId, direction, btnObj, visibleIf, typeActionButton) end local function Opener_OnReceiveDrag(self) @@ -420,6 +361,7 @@ local function Opener_OnDragStart(self) end local function Opener_UpdateFlyout(self) + -- print("========== Opener_UpdateFlyout()") this is being called continuously while a flyout exists on any bar -- Update border and determine arrow position local arrowDistance; -- Update border @@ -619,10 +561,11 @@ local snippet_Opener_Click = [=[ end ]=] -function FloFlyout:CreateOpener(name, idFlyout, actionId, direction, actionButton, actionBarPage, bonusBar, typeActionButton) +function FloFlyout:CreateOpener(actionId, idFlyout, direction, actionButton, visibleIf, typeActionButton) local flyoutConf = self.config.flyouts[idFlyout] - local opener = self.openers[name] or CreateFrame("CheckButton", name, UIParent, "ActionButtonTemplate, SecureHandlerClickTemplate") + local name = "FloFlyoutOpener"..actionId + local opener = self.openers[name] or CreateFrame("CheckButton", name, actionButton, "ActionButtonTemplate, SecureHandlerClickTemplate") self.openers[name] = opener opener.flyoutId = idFlyout opener.actionId = actionId @@ -644,15 +587,7 @@ function FloFlyout:CreateOpener(name, idFlyout, actionId, direction, actionButto end end - local strata - local thingOnCursor = GetCursorInfo() - if thingOnCursor then - strata = STRATA_MAX - else - strata = STRATA_DEFAULT - end - - opener:SetFrameStrata(strata) + opener:SetFrameStrata(STRATA_DEFAULT) opener:SetFrameLevel(100) opener:SetToplevel(true) @@ -693,18 +628,8 @@ function FloFlyout:CreateOpener(name, idFlyout, actionId, direction, actionButto icon:SetTexture(texture) end - local stateCondition = "nopetbattle,nooverridebar,novehicleui,nopossessbar" - if actionBarPage then - stateCondition = ",bar:"..actionBarPage - end - if bonusBar then - if bonusBar == 0 then - stateCondition = stateCondition..",nobonusbar:1,nobonusbar:2,nobonusbar:3,nobonusbar:4" - else - stateCondition = stateCondition..",bonusbar:"..bonusBar - end - end - if stateCondition ~= "" then + if visibleIf then + local stateCondition = "nopetbattle,nooverridebar,novehicleui,nopossessbar," .. visibleIf RegisterStateDriver(opener, "visibility", "["..stateCondition.."] show; hide") else opener:Show() @@ -904,7 +829,7 @@ function FloFlyoutButton_OnReceiveDrag(self) if oldMountIndex == nil then PickupSpell(oldActionData) else - C_MountJournal.Pickup(oldMountIndex) + C_MountJournal.Pickup(oldMountIndex) end elseif oldActionType == "item" then PickupItem(oldActionData) @@ -1127,11 +1052,11 @@ function FloFlyout.ConfigPane_Update() texture = FloFlyout:GetTexture(flyout.actionTypes[1], flyout.spells[1]) end if texture then - if(type(texture) == "number") then - button.icon:SetTexture(texture); - else - button.icon:SetTexture("INTERFACE\\ICONS\\"..texture); - end + if(type(texture) == "number") then + button.icon:SetTexture(texture); + else + button.icon:SetTexture("INTERFACE\\ICONS\\"..texture); + end else button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") end @@ -1176,7 +1101,7 @@ function FloFlyout.ConfigPane_Update() buttons[i].BgBottom:Hide() buttons[i].BgMiddle:SetPoint("BOTTOM") end - + if (i+scrollOffset)%2 == 0 then buttons[i].Stripe:SetTexture(STRIPE_COLOR.r, STRIPE_COLOR.g, STRIPE_COLOR.b) buttons[i].Stripe:SetAlpha(0.1) @@ -1278,7 +1203,7 @@ function RecalculateFloFlyoutConfigDialogPopup(iconTexture) popup:SetSelection(false, 1) end - --[[ + --[[ Scroll and ensure that any selected equipment shows up in the list. When we first press "save", we want to make sure any selected equipment set shows up in the list, so that the user can just make his changes and press Okay to overwrite. @@ -1315,7 +1240,7 @@ function RecalculateFloFlyoutConfigDialogPopup(iconTexture) end --[[ -RefreshFlyoutIconInfo() counts how many uniquely textured spells the player has in the current flyout. +RefreshFlyoutIconInfo() counts how many uniquely textured spells the player has in the current flyout. ]] function FloFlyout.RefreshFlyoutIconInfo() FC_ICON_FILENAMES = {} @@ -1428,7 +1353,7 @@ function FloFlyoutConfigPopupButton_OnClick(self, button, down) local popup = FloFlyoutConfigDialogPopup local offset = FauxScrollFrame_GetOffset(FloFlyoutConfigDialogPopupScrollFrame) or 0 popup.selectedIcon = (offset * NUM_FLYOUT_ICONS_PER_ROW) + self:GetID() - popup.selectedTexture = nil + popup.selectedTexture = nil FloFlyoutConfigDialogPopup_Update() FloFlyout.ConfigDialogPopupOkay_Update() end From df5b63674aec0dc6971ac5a1b9795c214e23eafe Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sun, 9 Apr 2023 22:41:35 -0700 Subject: [PATCH 09/23] throttle OnUpdate because it fires as often as FPS and is very resource intensive - fix for issue #11 --- FloFlyout.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/FloFlyout.lua b/FloFlyout.lua index 79f1bda..400db0b 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -429,6 +429,19 @@ local function Opener_UpdateFlyout(self) end end +-- throttle OnUpdate because it fires as often as FPS and is very resource intensive +local ON_UPDATE_TIMER_FREQUENCY = 1.5 +local onUpdateTimer = 0 +local function Opener_UpdateFlyout_OnUpdate(self, elapsed) + onUpdateTimer = onUpdateTimer + elapsed + if onUpdateTimer < ON_UPDATE_TIMER_FREQUENCY then + return + end + onUpdateTimer = 0 + print("=1======== Opener_UpdateFlyout_OnUpdate()") + Opener_UpdateFlyout(self) +end + local function Opener_PreClick(self, button, down) self:SetChecked(not self:GetChecked()) local direction = self:GetAttribute("flyoutDirection"); @@ -616,7 +629,8 @@ function FloFlyout:CreateOpener(name, idFlyout, actionId, direction, actionButto opener:SetAttribute("spellnamelist", strjoin(",", unpack(flyoutConf.spellNames))) opener:SetAttribute("typelist", strjoin(",", unpack(flyoutConf.actionTypes))) - opener:SetScript("OnUpdate", Opener_UpdateFlyout) + -- TODO: find a way to eliminate the need for OnUpdate + opener:SetScript("OnUpdate", Opener_UpdateFlyout_OnUpdate) opener:SetScript("OnEnter", Opener_UpdateFlyout) opener:SetScript("OnLeave", Opener_UpdateFlyout) From a6dfba0fe26e10e9a37c277b05110d1a83b8d0f4 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Sun, 9 Apr 2023 22:48:45 -0700 Subject: [PATCH 10/23] oops, left in a debug statement --- FloFlyout.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/FloFlyout.lua b/FloFlyout.lua index 400db0b..70cf94a 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -438,7 +438,6 @@ local function Opener_UpdateFlyout_OnUpdate(self, elapsed) return end onUpdateTimer = 0 - print("=1======== Opener_UpdateFlyout_OnUpdate()") Opener_UpdateFlyout(self) end From eca5bbc62f2df4e724dfcec84e390135b3ec55e7 Mon Sep 17 00:00:00 2001 From: Scott Ingram Date: Mon, 17 Apr 2023 22:14:52 -0700 Subject: [PATCH 11/23] import Ace libs to support config switching --- FloFlyout.lua | 308 ++- FloFlyout.toc | 4 +- Libs/AceAddon-3.0/AceAddon-3.0.lua | 674 ++++++ Libs/AceAddon-3.0/AceAddon-3.0.xml | 4 + Libs/AceConfig-3.0/AceConfig-3.0.lua | 58 + Libs/AceConfig-3.0/AceConfig-3.0.xml | 8 + .../AceConfigCmd-3.0/AceConfigCmd-3.0.lua | 794 +++++++ .../AceConfigCmd-3.0/AceConfigCmd-3.0.xml | 4 + .../AceConfigDialog-3.0.lua | 1983 +++++++++++++++++ .../AceConfigDialog-3.0.xml | 4 + .../AceConfigRegistry-3.0.lua | 350 +++ .../AceConfigRegistry-3.0.xml | 4 + Libs/AceConsole-3.0/AceConsole-3.0.lua | 250 +++ Libs/AceConsole-3.0/AceConsole-3.0.xml | 4 + Libs/AceDB-3.0/AceDB-3.0.lua | 746 +++++++ Libs/AceDB-3.0/AceDB-3.0.xml | 4 + Libs/AceDBOptions-3.0/AceDBOptions-3.0.lua | 460 ++++ Libs/AceDBOptions-3.0/AceDBOptions-3.0.xml | 4 + Libs/AceEvent-3.0/AceEvent-3.0.lua | 126 ++ Libs/AceEvent-3.0/AceEvent-3.0.xml | 4 + Libs/AceHook-3.0/AceHook-3.0.lua | 511 +++++ Libs/AceHook-3.0/AceHook-3.0.xml | 4 + Libs/LibStub/LibStub.lua | 51 + Libs/LibStub/LibStub.toc | 13 + embeds.xml | 10 + 25 files changed, 6301 insertions(+), 81 deletions(-) create mode 100755 Libs/AceAddon-3.0/AceAddon-3.0.lua create mode 100755 Libs/AceAddon-3.0/AceAddon-3.0.xml create mode 100755 Libs/AceConfig-3.0/AceConfig-3.0.lua create mode 100755 Libs/AceConfig-3.0/AceConfig-3.0.xml create mode 100755 Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.lua create mode 100755 Libs/AceConfig-3.0/AceConfigCmd-3.0/AceConfigCmd-3.0.xml create mode 100755 Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.lua create mode 100755 Libs/AceConfig-3.0/AceConfigDialog-3.0/AceConfigDialog-3.0.xml create mode 100755 Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.lua create mode 100755 Libs/AceConfig-3.0/AceConfigRegistry-3.0/AceConfigRegistry-3.0.xml create mode 100755 Libs/AceConsole-3.0/AceConsole-3.0.lua create mode 100755 Libs/AceConsole-3.0/AceConsole-3.0.xml create mode 100755 Libs/AceDB-3.0/AceDB-3.0.lua create mode 100755 Libs/AceDB-3.0/AceDB-3.0.xml create mode 100755 Libs/AceDBOptions-3.0/AceDBOptions-3.0.lua create mode 100755 Libs/AceDBOptions-3.0/AceDBOptions-3.0.xml create mode 100755 Libs/AceEvent-3.0/AceEvent-3.0.lua create mode 100755 Libs/AceEvent-3.0/AceEvent-3.0.xml create mode 100755 Libs/AceHook-3.0/AceHook-3.0.lua create mode 100755 Libs/AceHook-3.0/AceHook-3.0.xml create mode 100755 Libs/LibStub/LibStub.lua create mode 100755 Libs/LibStub/LibStub.toc create mode 100755 embeds.xml diff --git a/FloFlyout.lua b/FloFlyout.lua index b94cbdc..c2b5cf2 100755 --- a/FloFlyout.lua +++ b/FloFlyout.lua @@ -1,15 +1,20 @@ -- This Source Code Form is subject to the terms of the Mozilla Public -- License, v. 2.0. If a copy of the MPL was not distributed with this file, -- You can obtain one at http://mozilla.org/MPL/2.0/. + local MY_NAME, MY_GLOBALS = ... -local DEBUG = MY_GLOBALS.DEBUG -- my debugging routines that I don't check-in +local L = FLOFLYOUT_L10N_STRINGS -- auto loaded from locales directory + +FloFlyout = LibStub("AceAddon-3.0"):NewAddon(MY_NAME, "AceConsole-3.0", "AceEvent-3.0", "AceHook-3.0") +FloFlyout.openers = {} -- copies of flyouts that sit on the action bars ------------------------------------------------------------------------------- -- Constants ------------------------------------------------------------------------------- local VERSION = "10.0.16" -local NAME = "FloFlyout" +local NAME = MY_NAME +local MAX_FLYOUT_SIZE = 10 local SPELLFLYOUT_DEFAULT_SPACING = 4 local SPELLFLYOUT_INITIAL_SPACING = 7 local SPELLFLYOUT_FINAL_SPACING = 4 @@ -39,8 +44,10 @@ local BLIZ_BAR_METADATA = { ------------------------------------------------------------------------------- local _ local _classicUI +local Db = nil -- initialized by Ace in OnInitialize -FLOFLYOUT_CONFIG = { +DEFAULT_FLOFLYOUT_CONFIG = { + -- unique flyout definitions shown in the config panel flyouts = { --[[ Sample config : each flyout can have a list of actions and an icon [1] = { @@ -61,9 +68,11 @@ FLOFLYOUT_CONFIG = { }, ]] }, - actions = { + -- assigned action bar button slots + flyoutPlacements = { + -- each class spec has its own set of placements [1] = { - --[[ Sample config : for each talent group there is a list of actions bound to flyouts + --[[ Sample config : the bliz action bars have [button slot IDs] on which we can place a flyout ID (see above) [13] = 1, [49] = 1, [25] = 1, @@ -80,19 +89,155 @@ FLOFLYOUT_CONFIG = { } } -local FloFlyout = { - openers = {}, - config = FLOFLYOUT_CONFIG +------------------------------------------------------------------------------- +-- Ace -> Bliz Config UI definition +------------------------------------------------------------------------------- + +local escMenuConfigDef = { + name = MY_NAME, + type = "group", + args = { + enable = { + name = "Enable", + desc = "Enable / disables the addon", + type = "toggle", + set = function(info, val) + --db.profile.enabled = val + end, + get = function() + --return db.profile.enabled + end + }, + debug = { + name = "Show debug info", + desc = "Enable / disable debug information", + type = "toggle", + set = function(info, val) + Db.profile.debug = val + end, + get = function() + return Db.profile.debug + end + }, + profileUi = {} -- will be populated by Ace in OnInitialize() + } } -local L = FLOFLYOUT_L10N_STRINGS +local defaultConfigOptions = { + profile = { -- required by AceDB + debug = false + } +} ------------------------------------------------------------------------------- --- Functions +-- Ace Addon lifecycle ------------------------------------------------------------------------------- -function isEmpty(s) - return s == nil or s == '' +-- called by Ace directly after the addon is fully loaded +function FloFlyout:OnInitialize() + print("FloFlyout:OnInitialize() aces!") + + -- AceDB manages SavedVariables and adds profile management -- must match the .toc ## SavedVariables + Db = LibStub("AceDB-3.0"):New("FLOFLYOUT_ACCOUNT_CONFIG", defaultConfigOptions) + + -- grabs Ace's profile management UI def and adds it as another submenu of ours + escMenuConfigDef.args.profileUi = LibStub("AceDBOptions-3.0"):GetOptionsTable(Db) + + -- Ace-only config registry (required by AceConfigDialog below). Also adds slash commands {the, stuff, on, the, end} + LibStub("AceConfig-3.0"):RegisterOptionsTable(MY_NAME, escMenuConfigDef, { "ff", MY_NAME, string.lower(MY_NAME) }) + + -- inserts our custom config into the Bliz addon config UI + LibStub("AceConfigDialog-3.0"):AddToBlizOptions(MY_NAME) + + -- if the user switches to a different profile in the addon config options + Db.RegisterCallback(self, "OnProfileChanged", "HandleConfigChanges") + Db.RegisterCallback(self, "OnProfileCopied", "HandleConfigChanges") + Db.RegisterCallback(self, "OnProfileReset", "HandleConfigChanges") + + self:InitializeConfigDataIfEmpty(true) + self:AttachConfigToSelf() + self:InitializeFlyoutsGiveThemOnClickHandlers() +end + +--[[ +-- called by Ace during the PLAYER_LOGIN event, when most of the data provided by the game is already present +function FloFlyoutAce:OnEnable() +end + +-- called by Ace only when your addon is manually being disabled +function FloFlyoutAce:OnDisable() +end +]] + +------------------------------------------------------------------------------- +-- FloFlyout Methods +------------------------------------------------------------------------------- + +function FloFlyout:HandleConfigChanges() + self:InitializeConfigDataIfEmpty() + self:AttachConfigToSelf() + self:ApplyConfig() +end + +function FloFlyout:InitializeConfigDataIfEmpty(mayUseLegacyData) + if Db.profile.config then + return + end + + local newConfig + local legacyData = mayUseLegacyData and FLOFLYOUT_CONFIG + if legacyData then + newConfig = deepcopy(legacyData) + fixLegacyDataNils(newConfig) + -- rename the button slots from its legacy name + newConfig.flyoutPlacements = newConfig.actions + newConfig.actions = nil + else + newConfig = deepcopy(DEFAULT_FLOFLYOUT_CONFIG) + end + + Db.profile.config = newConfig + +end + +-- attach the new config object to the legacy spot so the existing code finds it where it expects. +function FloFlyout:AttachConfigToSelf() + self.flyoutPlacements = Db.profile.config.flyoutPlacements + self.flyouts = Db.profile.config.flyouts +end + +function FloFlyout:InitializeFlyoutsGiveThemOnClickHandlers() + for i, button in ipairs({FloFlyoutFrame:GetChildren()}) do + if button:GetObjectType() == "CheckButton" then + SecureHandlerWrapScript(button, "OnClick", button, "self:GetParent():Hide()") + end + end + + FloFlyoutConfigFlyoutFrame.IsConfig = true +end + +function fixLegacyDataNils(config) + for _, flyout in ipairs(config.flyouts) do + if flyout.actionTypes == nil then + flyout.actionTypes = {} + for i, _ in ipairs(flyout.spells) do + flyout.actionTypes[i] = "spell" + end + end + if flyout.mountIndex == nil then + flyout.mountIndex = {} + end + if flyout.spellNames == nil then + flyout.spellNames = {} + end + end + + local flyoutPlacements = config.flyoutPlacements or config.actions + for i=3,5 do + if flyoutPlacements[i] == nil then + flyoutPlacements[i] = {} + end + end end function FloFlyout.ReadCmd(line) @@ -144,7 +289,7 @@ function FloFlyout_OnLoad(self) whileDead = 1, } - self:RegisterEvent("ADDON_LOADED") + -- self:RegisterEvent("ADDON_LOADED") -- replaced with Ace self:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED") --self:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED") @@ -165,7 +310,7 @@ function FloFlyout_OnLoad(self) end -function FloFlyout_OnEvent(self, event, arg1, ...) +function FloFlyout_OnEvent(FloFlyoutListener, event, arg1, ...) if event == "PLAYER_ENTERING_WORLD" --[[or event == "PLAYER_ALIVE"]] then @@ -175,46 +320,11 @@ function FloFlyout_OnEvent(self, event, arg1, ...) --elseif event == "SPELL_UPDATE_COOLDOWN" or event == "ACTIONBAR_UPDATE_USABLE" then - elseif event == "ADDON_LOADED" and arg1 == NAME then - - self:UnregisterEvent("ADDON_LOADED") - -- Ici, nous avons rechargé notre configuration - FloFlyout.config = FLOFLYOUT_CONFIG - - for i, button in ipairs({FloFlyoutFrame:GetChildren()}) do - if button:GetObjectType() == "CheckButton" then - SecureHandlerWrapScript(button, "OnClick", button, "self:GetParent():Hide()") - end - end - - FloFlyoutConfigFlyoutFrame.IsConfig = true - - for _, flyout in ipairs(FloFlyout.config.flyouts) do - if flyout.actionTypes == nil then - flyout.actionTypes = {} - for i, _ in ipairs(flyout.spells) do - flyout.actionTypes[i] = "spell" - end - end - if flyout.mountIndex == nil then - flyout.mountIndex = {} - end - if flyout.spellNames == nil then - flyout.spellNames = {} - end - end - - for i=3,5 do - if FloFlyout.config.actions[i] == nil then - FloFlyout.config.actions[i] = {} - end - end - elseif event == "ACTIONBAR_SLOT_CHANGED" then local idAction = arg1 -- Dans tous les cas, si nous avions un flyout sur cette action, il faut l'enlever de l'action et le mettre dans le curseur local configChanged - local oldFlyoutId = FloFlyout.config.actions[GetSpecialization()][idAction] + local oldFlyoutId = FloFlyout.flyoutPlacements[GetSpecialization()][idAction] local actionType, id, subType = GetActionInfo(idAction) -- Si actionType vide, c'est sans doute que l'on vient de détruire la macro bidon @@ -577,13 +687,13 @@ local snippet_Opener_Click = [=[ end ]=] -function FloFlyout:CreateOpener(actionId, idFlyout, direction, actionButton, visibleIf, typeActionButton) +function FloFlyout:CreateOpener(actionId, flyoutId, direction, actionButton, visibleIf, typeActionButton) - local flyoutConf = self.config.flyouts[idFlyout] + local flyoutConf = self.flyouts[flyoutId] local name = "FloFlyoutOpener"..actionId local opener = self.openers[name] or CreateFrame("CheckButton", name, actionButton, "ActionButtonTemplate, SecureHandlerClickTemplate") self.openers[name] = opener - opener.flyoutId = idFlyout + opener.flyoutId = flyoutId opener.actionId = actionId if _classicUI then @@ -627,7 +737,7 @@ function FloFlyout:CreateOpener(actionId, idFlyout, direction, actionButton, vis opener:SetScript("OnLeave", Opener_UpdateFlyout) opener:SetScript("OnReceiveDrag", Opener_OnReceiveDrag) - opener:SetScript("OnMouseUp", Opener_OnReceiveDrag) + opener:SetScript("OnMouseUp", Opener_OnReceiveDrag) -- Hmmm... needed? opener:SetScript("OnDragStart", Opener_OnDragStart) opener:SetScript("PreClick", Opener_PreClick) @@ -667,37 +777,38 @@ function FloFlyout:ApplyConfig() return end self:ClearOpeners() - for a,f in pairs(self.config.actions[GetSpecialization()]) do + for a,f in pairs(self.flyoutPlacements[GetSpecialization()]) do self:BindFlyoutToAction(f, a) end end function FloFlyout:IsValidFlyoutId(arg1) local id = tonumber(arg1) - return id and self.config.flyouts[id] + return id and self.flyouts[id] end function FloFlyout:IsValidSpellPos(flyoutId, arg2) if type(flyoutId) == "string" then flyoutId = tonumber(flyoutId) end local pos = tonumber(arg2) - return pos and self.config.flyouts[flyoutId].spells[pos] + return pos and self.flyouts[flyoutId].spells[pos] end function FloFlyout:AddFlyout() - table.insert(self.config.flyouts, { spells = {}, actionTypes = {}, mountIndex = {}, spellNames = {} }) - return #self.config.flyouts + -- TODO: support macros and battle pets + table.insert(self.flyouts, { spells = {}, actionTypes = {}, mountIndex = {}, spellNames = {} }) + return #self.flyouts end function FloFlyout:RemoveFlyout(flyoutId) if type(flyoutId) == "string" then flyoutId = tonumber(flyoutId) end - table.remove(self.config.flyouts, flyoutId) + table.remove(self.flyouts, flyoutId) -- shift references for i = 1, 3 do - for a,f in pairs(self.config.actions[i]) do + for a,f in pairs(self.flyoutPlacements[i]) do if f == flyoutId then - self.config.actions[i][a] = nil + self.flyoutPlacements[i][a] = nil elseif f > flyoutId then - self.config.actions[i][a] = f - 1 + self.flyoutPlacements[i][a] = f - 1 end end end @@ -706,7 +817,7 @@ end function FloFlyout:AddSpell(flyoutId, actionType, spellId) if type(flyoutId) == "string" then flyoutId = tonumber(flyoutId) end if type(spellId) == "string" then spellId = tonumber(spellId) end - local flyoutConf = self.config.flyouts[flyoutId] + local flyoutConf = self.flyouts[flyoutId] table.insert(flyoutConf.spells, spellId) local newPos = #flyoutConf.spells flyoutConf.actionTypes[newPos] = actionType @@ -716,7 +827,8 @@ end function FloFlyout:RemoveSpell(flyoutId, spellPos) if type(flyoutId) == "string" then flyoutId = tonumber(flyoutId) end if type(spellPos) == "string" then spellPos = tonumber(spellPos) end - local flyoutConf = self.config.flyouts[flyoutId] + local flyoutConf = self.flyouts[flyoutId] + -- TODO: support macros and battle pets table.remove(flyoutConf.spells, spellPos) table.remove(flyoutConf.actionTypes, spellPos) table.remove(flyoutConf.mountIndex, spellPos) @@ -726,12 +838,12 @@ end function FloFlyout:AddAction(actionId, flyoutId) if type(actionId) == "string" then actionId = tonumber(actionId) end if type(flyoutId) == "string" then flyoutId = tonumber(flyoutId) end - self.config.actions[GetSpecialization()][actionId] = flyoutId + self.flyoutPlacements[GetSpecialization()][actionId] = flyoutId end function FloFlyout:RemoveAction(actionId) if type(actionId) == "string" then actionId = tonumber(actionId) end - self.config.actions[GetSpecialization()][actionId] = nil + self.flyoutPlacements[GetSpecialization()][actionId] = nil end function FloFlyout:PickupFlyout(flyoutId) @@ -740,7 +852,7 @@ function FloFlyout:PickupFlyout(flyoutId) return; end - local flyoutConf = self.config.flyouts[flyoutId] + local flyoutConf = self.flyouts[flyoutId] local texture = flyoutConf.icon if not texture and flyoutConf.spells[1] then @@ -755,6 +867,7 @@ function FloFlyout:PickupFlyout(flyoutId) PickupMacro(macroId) end +-- TODO: support macros and battle pets function FloFlyoutButton_SetTooltip(self) if GetCVar("UberTooltips") == "1" then GameTooltip_SetDefaultAnchor(GameTooltip, self) @@ -783,6 +896,7 @@ function FloFlyoutButton_SetTooltip(self) end end +-- TODO: support macros and battle pets function FloFlyoutButton_OnDragStart(self) if InCombatLockdown() then return end @@ -800,6 +914,8 @@ function FloFlyoutButton_OnDragStart(self) PickupItem(spell) end + --print("#### FloFlyoutButton_OnDragStart--> actionType =",actionType, " spellID =",spell, " mountIndex =,mountIndex") + local parent = self:GetParent() if parent.IsConfig then FloFlyout:RemoveSpell(parent.idFlyout, self:GetID()) @@ -820,6 +936,9 @@ function FloFlyoutButton_OnReceiveDrag(self) local kind, info1, info2, info3 = GetCursorInfo() local actionType, actionData, mountIndex + + -- support battle pets and macros + --print("FloFlyoutButton_OnReceiveDrag--> kind =",kind, " -- info1 =",info1, " -- info1 =",info1, " -- info3 =",info3) if kind == "spell" then actionType = "spell" actionData = info3 @@ -832,7 +951,7 @@ function FloFlyoutButton_OnReceiveDrag(self) actionData = info1 end if actionType then - local flyoutConf = FloFlyout.config.flyouts[parent.idFlyout] + local flyoutConf = FloFlyout.flyouts[parent.idFlyout] local oldActionData = flyoutConf.spells[self:GetID()] local oldActionType = flyoutConf.actionTypes[self:GetID()] local oldMountIndex = flyoutConf.mountIndex[self:GetID()] @@ -856,6 +975,7 @@ function FloFlyoutButton_OnReceiveDrag(self) end end +-- TODO: support macros and battle pets function FloFlyoutConfigFlyoutFrame_Update(self, idFlyout) local direction = "RIGHT" local parent = self.parent @@ -865,11 +985,11 @@ function FloFlyoutConfigFlyoutFrame_Update(self, idFlyout) -- Update all spell buttons for this flyout local prevButton = nil; local numButtons = 0; - local spells = FloFlyout.config.flyouts[idFlyout].spells - local actionTypes = FloFlyout.config.flyouts[idFlyout].actionTypes - local mountIndexes = FloFlyout.config.flyouts[idFlyout].mountIndex + local spells = FloFlyout.flyouts[idFlyout].spells + local actionTypes = FloFlyout.flyouts[idFlyout].actionTypes + local mountIndexes = FloFlyout.flyouts[idFlyout].mountIndex - for i=1, math.min(#spells+1, 10) do + for i=1, math.min(#spells+1, MAX_FLYOUT_SIZE) do local spellID = spells[i] local actionType = actionTypes[i] local mountIndex = mountIndexes[i] @@ -904,8 +1024,9 @@ function FloFlyoutConfigFlyoutFrame_Update(self, idFlyout) button:Show() + -- TODO: support macros and battle pets if spellID then - button.spellID = spellID + button.spellID = spellID -- this is read by Bliz code in SpellFlyout.lua button.actionType = actionType button.mountIndex = mountIndex local texture = FloFlyout:GetTexture(actionType, spellID) @@ -1045,7 +1166,7 @@ function FloFlyoutConfigPane_OnUpdate(self) end function FloFlyout.ConfigPane_Update() - local numRows = #FloFlyout.config.flyouts + 1 + local numRows = #FloFlyout.flyouts + 1 HybridScrollFrame_Update(FloFlyoutConfigPane, numRows * EQUIPMENTSET_BUTTON_HEIGHT + 20, FloFlyoutConfigPane:GetHeight()) local scrollOffset = HybridScrollFrame_GetOffset(FloFlyoutConfigPane) @@ -1064,7 +1185,7 @@ function FloFlyout.ConfigPane_Update() button.name = i+scrollOffset button.text:SetText(button.name); button.text:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b); - flyout = FloFlyout.config.flyouts[i+scrollOffset] + flyout = FloFlyout.flyouts[i+scrollOffset] texture = flyout.icon if not texture and flyout.spells[1] then @@ -1268,8 +1389,8 @@ function FloFlyout.RefreshFlyoutIconInfo() local popup = FloFlyoutConfigDialogPopup if popup.name then - local spells = FloFlyout.config.flyouts[popup.name].spells - local actionTypes = FloFlyout.config.flyouts[popup.name].actionTypes + local spells = FloFlyout.flyouts[popup.name].spells + local actionTypes = FloFlyout.flyouts[popup.name].actionTypes for i = 1, #spells do local itemTexture = FloFlyout:GetTexture(actionTypes[i], spells[i]) if itemTexture then @@ -1354,10 +1475,10 @@ function FloFlyoutConfigDialogPopupOkay_OnClick(self, button, pushed) if popup.isEdit then -- Modifying a flyout - FloFlyout.config.flyouts[popup.name].icon = iconTexture + FloFlyout.flyouts[popup.name].icon = iconTexture else -- Saving a new flyout - FloFlyout.config.flyouts[FloFlyout:AddFlyout()].icon = iconTexture + FloFlyout.flyouts[FloFlyout:AddFlyout()].icon = iconTexture end popup:Hide() FloFlyout.ConfigPane_Update() @@ -1377,4 +1498,31 @@ function FloFlyoutConfigPopupButton_OnClick(self, button, down) FloFlyout.ConfigDialogPopupOkay_Update() end +------------------------------------------------------------------------------- +-- Utility Functions +------------------------------------------------------------------------------- + +function deepcopy(orig) + local orig_type = type(orig) + local copy + if orig_type == 'table' then + copy = {} + for orig_key, orig_value in next, orig, nil do + copy[deepcopy(orig_key)] = deepcopy(orig_value) + end + setmetatable(copy, deepcopy(getmetatable(orig))) + else -- number, string, boolean, etc + copy = orig + end + return copy +end + +function isEmpty(s) + return s == nil or s == '' +end +-- TODO: support macros and BP +-- in SpellFlyout.lua +-- SpellFlyoutButton_OnClick +-- is responsible for casting spells, but knows nothing of pets or macros... but somehow understands mounts... because mounts are covered by self.spellName +-- so, I could override it, check for a custom attribute self.petId or self.macroId ... win? diff --git a/FloFlyout.toc b/FloFlyout.toc index dd291a7..fb583f5 100755 --- a/FloFlyout.toc +++ b/FloFlyout.toc @@ -4,6 +4,8 @@ ## Notes: Create custom Flyout buttons ## OptionalDeps: ClassicUI ## Dependencies: -## SavedVariables: +## SavedVariables: FLOFLYOUT_ACCOUNT_CONFIG ## SavedVariablesPerCharacter: FLOFLYOUT_CONFIG +## Comment: SavedVariablesPerCharacter only exists for legacy support +embeds.xml FloFlyout.xml diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.lua b/Libs/AceAddon-3.0/AceAddon-3.0.lua new file mode 100755 index 0000000..a7f7279 --- /dev/null +++ b/Libs/AceAddon-3.0/AceAddon-3.0.lua @@ -0,0 +1,674 @@ +--- **AceAddon-3.0** provides a template for creating addon objects. +-- It'll provide you with a set of callback functions that allow you to simplify the loading +-- process of your addon.\\ +-- Callbacks provided are:\\ +-- * **OnInitialize**, which is called directly after the addon is fully loaded. +-- * **OnEnable** which gets called during the PLAYER_LOGIN event, when most of the data provided by the game is already present. +-- * **OnDisable**, which is only called when your addon is manually being disabled. +-- @usage +-- -- A small (but complete) addon, that doesn't do anything, +-- -- but shows usage of the callbacks. +-- local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon") +-- +-- function MyAddon:OnInitialize() +-- -- do init tasks here, like loading the Saved Variables, +-- -- or setting up slash commands. +-- end +-- +-- function MyAddon:OnEnable() +-- -- Do more initialization here, that really enables the use of your addon. +-- -- Register Events, Hook functions, Create Frames, Get information from +-- -- the game that wasn't available in OnInitialize +-- end +-- +-- function MyAddon:OnDisable() +-- -- Unhook, Unregister Events, Hide frames that you created. +-- -- You would probably only use an OnDisable if you want to +-- -- build a "standby" mode, or be able to toggle modules on/off. +-- end +-- @class file +-- @name AceAddon-3.0.lua +-- @release $Id: AceAddon-3.0.lua 1084 2013-04-27 20:14:11Z nevcairiel $ + +local MAJOR, MINOR = "AceAddon-3.0", 12 +local AceAddon, oldminor = LibStub:NewLibrary(MAJOR, MINOR) + +if not AceAddon then return end -- No Upgrade needed. + +AceAddon.frame = AceAddon.frame or CreateFrame("Frame", "AceAddon30Frame") -- Our very own frame +AceAddon.addons = AceAddon.addons or {} -- addons in general +AceAddon.statuses = AceAddon.statuses or {} -- statuses of addon. +AceAddon.initializequeue = AceAddon.initializequeue or {} -- addons that are new and not initialized +AceAddon.enablequeue = AceAddon.enablequeue or {} -- addons that are initialized and waiting to be enabled +AceAddon.embeds = AceAddon.embeds or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end }) -- contains a list of libraries embedded in an addon + +-- Lua APIs +local tinsert, tconcat, tremove = table.insert, table.concat, table.remove +local fmt, tostring = string.format, tostring +local select, pairs, next, type, unpack = select, pairs, next, type, unpack +local loadstring, assert, error = loadstring, assert, error +local setmetatable, getmetatable, rawset, rawget = setmetatable, getmetatable, rawset, rawget + +-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded +-- List them here for Mikk's FindGlobals script +-- GLOBALS: LibStub, IsLoggedIn, geterrorhandler + +--[[ + xpcall safecall implementation +]] +local xpcall = xpcall + +local function errorhandler(err) + return geterrorhandler()(err) +end + +local function CreateDispatcher(argCount) + local code = [[ + local xpcall, eh = ... + local method, ARGS + local function call() return method(ARGS) end + + local function dispatch(func, ...) + method = func + if not method then return end + ARGS = ... + return xpcall(call, eh) + end + + return dispatch + ]] + + local ARGS = {} + for i = 1, argCount do ARGS[i] = "arg"..i end + code = code:gsub("ARGS", tconcat(ARGS, ", ")) + return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) +end + +local Dispatchers = setmetatable({}, {__index=function(self, argCount) + local dispatcher = CreateDispatcher(argCount) + rawset(self, argCount, dispatcher) + return dispatcher +end}) +Dispatchers[0] = function(func) + return xpcall(func, errorhandler) +end + +local function safecall(func, ...) + -- we check to see if the func is passed is actually a function here and don't error when it isn't + -- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not + -- present execution should continue without hinderance + if type(func) == "function" then + return Dispatchers[select('#', ...)](func, ...) + end +end + +-- local functions that will be implemented further down +local Enable, Disable, EnableModule, DisableModule, Embed, NewModule, GetModule, GetName, SetDefaultModuleState, SetDefaultModuleLibraries, SetEnabledState, SetDefaultModulePrototype + +-- used in the addon metatable +local function addontostring( self ) return self.name end + +-- Check if the addon is queued for initialization +local function queuedForInitialization(addon) + for i = 1, #AceAddon.initializequeue do + if AceAddon.initializequeue[i] == addon then + return true + end + end + return false +end + +--- Create a new AceAddon-3.0 addon. +-- Any libraries you specified will be embeded, and the addon will be scheduled for +-- its OnInitialize and OnEnable callbacks. +-- The final addon object, with all libraries embeded, will be returned. +-- @paramsig [object ,]name[, lib, ...] +-- @param object Table to use as a base for the addon (optional) +-- @param name Name of the addon object to create +-- @param lib List of libraries to embed into the addon +-- @usage +-- -- Create a simple addon object +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceEvent-3.0") +-- +-- -- Create a Addon object based on the table of a frame +-- local MyFrame = CreateFrame("Frame") +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon(MyFrame, "MyAddon", "AceEvent-3.0") +function AceAddon:NewAddon(objectorname, ...) + local object,name + local i=1 + if type(objectorname)=="table" then + object=objectorname + name=... + i=2 + else + name=objectorname + end + if type(name)~="string" then + error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) + end + if self.addons[name] then + error(("Usage: NewAddon([object,] name, [lib, lib, lib, ...]): 'name' - Addon '%s' already exists."):format(name), 2) + end + + object = object or {} + object.name = name + + local addonmeta = {} + local oldmeta = getmetatable(object) + if oldmeta then + for k, v in pairs(oldmeta) do addonmeta[k] = v end + end + addonmeta.__tostring = addontostring + + setmetatable( object, addonmeta ) + self.addons[name] = object + object.modules = {} + object.orderedModules = {} + object.defaultModuleLibraries = {} + Embed( object ) -- embed NewModule, GetModule methods + self:EmbedLibraries(object, select(i,...)) + + -- add to queue of addons to be initialized upon ADDON_LOADED + tinsert(self.initializequeue, object) + return object +end + + +--- Get the addon object by its name from the internal AceAddon registry. +-- Throws an error if the addon object cannot be found (except if silent is set). +-- @param name unique name of the addon object +-- @param silent if true, the addon is optional, silently return nil if its not found +-- @usage +-- -- Get the Addon +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +function AceAddon:GetAddon(name, silent) + if not silent and not self.addons[name] then + error(("Usage: GetAddon(name): 'name' - Cannot find an AceAddon '%s'."):format(tostring(name)), 2) + end + return self.addons[name] +end + +-- - Embed a list of libraries into the specified addon. +-- This function will try to embed all of the listed libraries into the addon +-- and error if a single one fails. +-- +-- **Note:** This function is for internal use by :NewAddon/:NewModule +-- @paramsig addon, [lib, ...] +-- @param addon addon object to embed the libs in +-- @param lib List of libraries to embed into the addon +function AceAddon:EmbedLibraries(addon, ...) + for i=1,select("#", ... ) do + local libname = select(i, ...) + self:EmbedLibrary(addon, libname, false, 4) + end +end + +-- - Embed a library into the addon object. +-- This function will check if the specified library is registered with LibStub +-- and if it has a :Embed function to call. It'll error if any of those conditions +-- fails. +-- +-- **Note:** This function is for internal use by :EmbedLibraries +-- @paramsig addon, libname[, silent[, offset]] +-- @param addon addon object to embed the library in +-- @param libname name of the library to embed +-- @param silent marks an embed to fail silently if the library doesn't exist (optional) +-- @param offset will push the error messages back to said offset, defaults to 2 (optional) +function AceAddon:EmbedLibrary(addon, libname, silent, offset) + local lib = LibStub:GetLibrary(libname, true) + if not lib and not silent then + error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Cannot find a library instance of %q."):format(tostring(libname)), offset or 2) + elseif lib and type(lib.Embed) == "function" then + lib:Embed(addon) + tinsert(self.embeds[addon], libname) + return true + elseif lib then + error(("Usage: EmbedLibrary(addon, libname, silent, offset): 'libname' - Library '%s' is not Embed capable"):format(libname), offset or 2) + end +end + +--- Return the specified module from an addon object. +-- Throws an error if the addon object cannot be found (except if silent is set) +-- @name //addon//:GetModule +-- @paramsig name[, silent] +-- @param name unique name of the module +-- @param silent if true, the module is optional, silently return nil if its not found (optional) +-- @usage +-- -- Get the Addon +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- -- Get the Module +-- MyModule = MyAddon:GetModule("MyModule") +function GetModule(self, name, silent) + if not self.modules[name] and not silent then + error(("Usage: GetModule(name, silent): 'name' - Cannot find module '%s'."):format(tostring(name)), 2) + end + return self.modules[name] +end + +local function IsModuleTrue(self) return true end + +--- Create a new module for the addon. +-- The new module can have its own embeded libraries and/or use a module prototype to be mixed into the module.\\ +-- A module has the same functionality as a real addon, it can have modules of its own, and has the same API as +-- an addon object. +-- @name //addon//:NewModule +-- @paramsig name[, prototype|lib[, lib, ...]] +-- @param name unique name of the module +-- @param prototype object to derive this module from, methods and values from this table will be mixed into the module (optional) +-- @param lib List of libraries to embed into the addon +-- @usage +-- -- Create a module with some embeded libraries +-- MyModule = MyAddon:NewModule("MyModule", "AceEvent-3.0", "AceHook-3.0") +-- +-- -- Create a module with a prototype +-- local prototype = { OnEnable = function(self) print("OnEnable called!") end } +-- MyModule = MyAddon:NewModule("MyModule", prototype, "AceEvent-3.0", "AceHook-3.0") +function NewModule(self, name, prototype, ...) + if type(name) ~= "string" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - string expected got '%s'."):format(type(name)), 2) end + if type(prototype) ~= "string" and type(prototype) ~= "table" and type(prototype) ~= "nil" then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'prototype' - table (prototype), string (lib) or nil expected got '%s'."):format(type(prototype)), 2) end + + if self.modules[name] then error(("Usage: NewModule(name, [prototype, [lib, lib, lib, ...]): 'name' - Module '%s' already exists."):format(name), 2) end + + -- modules are basically addons. We treat them as such. They will be added to the initializequeue properly as well. + -- NewModule can only be called after the parent addon is present thus the modules will be initialized after their parent is. + local module = AceAddon:NewAddon(fmt("%s_%s", self.name or tostring(self), name)) + + module.IsModule = IsModuleTrue + module:SetEnabledState(self.defaultModuleState) + module.moduleName = name + + if type(prototype) == "string" then + AceAddon:EmbedLibraries(module, prototype, ...) + else + AceAddon:EmbedLibraries(module, ...) + end + AceAddon:EmbedLibraries(module, unpack(self.defaultModuleLibraries)) + + if not prototype or type(prototype) == "string" then + prototype = self.defaultModulePrototype or nil + end + + if type(prototype) == "table" then + local mt = getmetatable(module) + mt.__index = prototype + setmetatable(module, mt) -- More of a Base class type feel. + end + + safecall(self.OnModuleCreated, self, module) -- Was in Ace2 and I think it could be a cool thing to have handy. + self.modules[name] = module + tinsert(self.orderedModules, module) + + return module +end + +--- Returns the real name of the addon or module, without any prefix. +-- @name //addon//:GetName +-- @paramsig +-- @usage +-- print(MyAddon:GetName()) +-- -- prints "MyAddon" +function GetName(self) + return self.moduleName or self.name +end + +--- Enables the Addon, if possible, return true or false depending on success. +-- This internally calls AceAddon:EnableAddon(), thus dispatching a OnEnable callback +-- and enabling all modules of the addon (unless explicitly disabled).\\ +-- :Enable() also sets the internal `enableState` variable to true +-- @name //addon//:Enable +-- @paramsig +-- @usage +-- -- Enable MyModule +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyModule = MyAddon:GetModule("MyModule") +-- MyModule:Enable() +function Enable(self) + self:SetEnabledState(true) + + -- nevcairiel 2013-04-27: don't enable an addon/module if its queued for init still + -- it'll be enabled after the init process + if not queuedForInitialization(self) then + return AceAddon:EnableAddon(self) + end +end + +--- Disables the Addon, if possible, return true or false depending on success. +-- This internally calls AceAddon:DisableAddon(), thus dispatching a OnDisable callback +-- and disabling all modules of the addon.\\ +-- :Disable() also sets the internal `enableState` variable to false +-- @name //addon//:Disable +-- @paramsig +-- @usage +-- -- Disable MyAddon +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyAddon:Disable() +function Disable(self) + self:SetEnabledState(false) + return AceAddon:DisableAddon(self) +end + +--- Enables the Module, if possible, return true or false depending on success. +-- Short-hand function that retrieves the module via `:GetModule` and calls `:Enable` on the module object. +-- @name //addon//:EnableModule +-- @paramsig name +-- @usage +-- -- Enable MyModule using :GetModule +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyModule = MyAddon:GetModule("MyModule") +-- MyModule:Enable() +-- +-- -- Enable MyModule using the short-hand +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyAddon:EnableModule("MyModule") +function EnableModule(self, name) + local module = self:GetModule( name ) + return module:Enable() +end + +--- Disables the Module, if possible, return true or false depending on success. +-- Short-hand function that retrieves the module via `:GetModule` and calls `:Disable` on the module object. +-- @name //addon//:DisableModule +-- @paramsig name +-- @usage +-- -- Disable MyModule using :GetModule +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyModule = MyAddon:GetModule("MyModule") +-- MyModule:Disable() +-- +-- -- Disable MyModule using the short-hand +-- MyAddon = LibStub("AceAddon-3.0"):GetAddon("MyAddon") +-- MyAddon:DisableModule("MyModule") +function DisableModule(self, name) + local module = self:GetModule( name ) + return module:Disable() +end + +--- Set the default libraries to be mixed into all modules created by this object. +-- Note that you can only change the default module libraries before any module is created. +-- @name //addon//:SetDefaultModuleLibraries +-- @paramsig lib[, lib, ...] +-- @param lib List of libraries to embed into the addon +-- @usage +-- -- Create the addon object +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon") +-- -- Configure default libraries for modules (all modules need AceEvent-3.0) +-- MyAddon:SetDefaultModuleLibraries("AceEvent-3.0") +-- -- Create a module +-- MyModule = MyAddon:NewModule("MyModule") +function SetDefaultModuleLibraries(self, ...) + if next(self.modules) then + error("Usage: SetDefaultModuleLibraries(...): cannot change the module defaults after a module has been registered.", 2) + end + self.defaultModuleLibraries = {...} +end + +--- Set the default state in which new modules are being created. +-- Note that you can only change the default state before any module is created. +-- @name //addon//:SetDefaultModuleState +-- @paramsig state +-- @param state Default state for new modules, true for enabled, false for disabled +-- @usage +-- -- Create the addon object +-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon") +-- -- Set the default state to "disabled" +-- MyAddon:SetDefaultModuleState(false) +-- -- Create a module and explicilty enable it +-- MyModule = MyAddon:NewModule("MyModule") +-- MyModule:Enable() +function SetDefaultModuleState(self, state) + if next(self.modules) then + error("Usage: SetDefaultModuleState(state): cannot change the module defaults after a module has been registered.", 2) + end + self.defaultModuleState = state +end + +--- Set the default prototype to use for new modules on creation. +-- Note that you can only change the default prototype before any module is created. +-- @name //addon//:SetDefaultModulePrototype +-- @paramsig prototype +-- @param prototype Default prototype for the new modules (table) +-- @usage +-- -- Define a prototype +-- local prototype = { OnEnable = function(self) print("OnEnable called!") end } +-- -- Set the default prototype +-- MyAddon:SetDefaultModulePrototype(prototype) +-- -- Create a module and explicitly Enable it +-- MyModule = MyAddon:NewModule("MyModule") +-- MyModule:Enable() +-- -- should print "OnEnable called!" now +-- @see NewModule +function SetDefaultModulePrototype(self, prototype) + if next(self.modules) then + error("Usage: SetDefaultModulePrototype(prototype): cannot change the module defaults after a module has been registered.", 2) + end + if type(prototype) ~= "table" then + error(("Usage: SetDefaultModulePrototype(prototype): 'prototype' - table expected got '%s'."):format(type(prototype)), 2) + end + self.defaultModulePrototype = prototype +end + +--- Set the state of an addon or module +-- This should only be called before any enabling actually happend, e.g. in/before OnInitialize. +-- @name //addon//:SetEnabledState +-- @paramsig state +-- @param state the state of an addon or module (enabled=true, disabled=false) +function SetEnabledState(self, state) + self.enabledState = state +end + + +--- Return an iterator of all modules associated to the addon. +-- @name //addon//:IterateModules +-- @paramsig +-- @usage +-- -- Enable all modules +-- for name, module in MyAddon:IterateModules() do +-- module:Enable() +-- end +local function IterateModules(self) return pairs(self.modules) end + +-- Returns an iterator of all embeds in the addon +-- @name //addon//:IterateEmbeds +-- @paramsig +local function IterateEmbeds(self) return pairs(AceAddon.embeds[self]) end + +--- Query the enabledState of an addon. +-- @name //addon//:IsEnabled +-- @paramsig +-- @usage +-- if MyAddon:IsEnabled() then +-- MyAddon:Disable() +-- end +local function IsEnabled(self) return self.enabledState end +local mixins = { + NewModule = NewModule, + GetModule = GetModule, + Enable = Enable, + Disable = Disable, + EnableModule = EnableModule, + DisableModule = DisableModule, + IsEnabled = IsEnabled, + SetDefaultModuleLibraries = SetDefaultModuleLibraries, + SetDefaultModuleState = SetDefaultModuleState, + SetDefaultModulePrototype = SetDefaultModulePrototype, + SetEnabledState = SetEnabledState, + IterateModules = IterateModules, + IterateEmbeds = IterateEmbeds, + GetName = GetName, +} +local function IsModule(self) return false end +local pmixins = { + defaultModuleState = true, + enabledState = true, + IsModule = IsModule, +} +-- Embed( target ) +-- target (object) - target object to embed aceaddon in +-- +-- this is a local function specifically since it's meant to be only called internally +function Embed(target, skipPMixins) + for k, v in pairs(mixins) do + target[k] = v + end + if not skipPMixins then + for k, v in pairs(pmixins) do + target[k] = target[k] or v + end + end +end + + +-- - Initialize the addon after creation. +-- This function is only used internally during the ADDON_LOADED event +-- It will call the **OnInitialize** function on the addon object (if present), +-- and the **OnEmbedInitialize** function on all embeded libraries. +-- +-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing. +-- @param addon addon object to intialize +function AceAddon:InitializeAddon(addon) + safecall(addon.OnInitialize, addon) + + local embeds = self.embeds[addon] + for i = 1, #embeds do + local lib = LibStub:GetLibrary(embeds[i], true) + if lib then safecall(lib.OnEmbedInitialize, lib, addon) end + end + + -- we don't call InitializeAddon on modules specifically, this is handled + -- from the event handler and only done _once_ +end + +-- - Enable the addon after creation. +-- Note: This function is only used internally during the PLAYER_LOGIN event, or during ADDON_LOADED, +-- if IsLoggedIn() already returns true at that point, e.g. for LoD Addons. +-- It will call the **OnEnable** function on the addon object (if present), +-- and the **OnEmbedEnable** function on all embeded libraries.\\ +-- This function does not toggle the enable state of the addon itself, and will return early if the addon is disabled. +-- +-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing. +-- Use :Enable on the addon itself instead. +-- @param addon addon object to enable +function AceAddon:EnableAddon(addon) + if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end + if self.statuses[addon.name] or not addon.enabledState then return false end + + -- set the statuses first, before calling the OnEnable. this allows for Disabling of the addon in OnEnable. + self.statuses[addon.name] = true + + safecall(addon.OnEnable, addon) + + -- make sure we're still enabled before continueing + if self.statuses[addon.name] then + local embeds = self.embeds[addon] + for i = 1, #embeds do + local lib = LibStub:GetLibrary(embeds[i], true) + if lib then safecall(lib.OnEmbedEnable, lib, addon) end + end + + -- enable possible modules. + local modules = addon.orderedModules + for i = 1, #modules do + self:EnableAddon(modules[i]) + end + end + return self.statuses[addon.name] -- return true if we're disabled +end + +-- - Disable the addon +-- Note: This function is only used internally. +-- It will call the **OnDisable** function on the addon object (if present), +-- and the **OnEmbedDisable** function on all embeded libraries.\\ +-- This function does not toggle the enable state of the addon itself, and will return early if the addon is still enabled. +-- +-- **Note:** Do not call this function manually, unless you're absolutely sure that you know what you are doing. +-- Use :Disable on the addon itself instead. +-- @param addon addon object to enable +function AceAddon:DisableAddon(addon) + if type(addon) == "string" then addon = AceAddon:GetAddon(addon) end + if not self.statuses[addon.name] then return false end + + -- set statuses first before calling OnDisable, this allows for aborting the disable in OnDisable. + self.statuses[addon.name] = false + + safecall( addon.OnDisable, addon ) + + -- make sure we're still disabling... + if not self.statuses[addon.name] then + local embeds = self.embeds[addon] + for i = 1, #embeds do + local lib = LibStub:GetLibrary(embeds[i], true) + if lib then safecall(lib.OnEmbedDisable, lib, addon) end + end + -- disable possible modules. + local modules = addon.orderedModules + for i = 1, #modules do + self:DisableAddon(modules[i]) + end + end + + return not self.statuses[addon.name] -- return true if we're disabled +end + +--- Get an iterator over all registered addons. +-- @usage +-- -- Print a list of all installed AceAddon's +-- for name, addon in AceAddon:IterateAddons() do +-- print("Addon: " .. name) +-- end +function AceAddon:IterateAddons() return pairs(self.addons) end + +--- Get an iterator over the internal status registry. +-- @usage +-- -- Print a list of all enabled addons +-- for name, status in AceAddon:IterateAddonStatus() do +-- if status then +-- print("EnabledAddon: " .. name) +-- end +-- end +function AceAddon:IterateAddonStatus() return pairs(self.statuses) end + +-- Following Iterators are deprecated, and their addon specific versions should be used +-- e.g. addon:IterateEmbeds() instead of :IterateEmbedsOnAddon(addon) +function AceAddon:IterateEmbedsOnAddon(addon) return pairs(self.embeds[addon]) end +function AceAddon:IterateModulesOfAddon(addon) return pairs(addon.modules) end + +-- Event Handling +local function onEvent(this, event, arg1) + -- 2011-08-17 nevcairiel - ignore the load event of Blizzard_DebugTools, so a potential startup error isn't swallowed up + if (event == "ADDON_LOADED" and arg1 ~= "Blizzard_DebugTools") or event == "PLAYER_LOGIN" then + -- if a addon loads another addon, recursion could happen here, so we need to validate the table on every iteration + while(#AceAddon.initializequeue > 0) do + local addon = tremove(AceAddon.initializequeue, 1) + -- this might be an issue with recursion - TODO: validate + if event == "ADDON_LOADED" then addon.baseName = arg1 end + AceAddon:InitializeAddon(addon) + tinsert(AceAddon.enablequeue, addon) + end + + if IsLoggedIn() then + while(#AceAddon.enablequeue > 0) do + local addon = tremove(AceAddon.enablequeue, 1) + AceAddon:EnableAddon(addon) + end + end + end +end + +AceAddon.frame:RegisterEvent("ADDON_LOADED") +AceAddon.frame:RegisterEvent("PLAYER_LOGIN") +AceAddon.frame:SetScript("OnEvent", onEvent) + +-- upgrade embeded +for name, addon in pairs(AceAddon.addons) do + Embed(addon, true) +end + +-- 2010-10-27 nevcairiel - add new "orderedModules" table +if oldminor and oldminor < 10 then + for name, addon in pairs(AceAddon.addons) do + addon.orderedModules = {} + for module_name, module in pairs(addon.modules) do + tinsert(addon.orderedModules, module) + end + end +end diff --git a/Libs/AceAddon-3.0/AceAddon-3.0.xml b/Libs/AceAddon-3.0/AceAddon-3.0.xml new file mode 100755 index 0000000..dcf24c7 --- /dev/null +++ b/Libs/AceAddon-3.0/AceAddon-3.0.xml @@ -0,0 +1,4 @@ + +