I'm trying to disable my addon without success. Am I missing something?
MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceComm-3.0", "AceSerializer-3.0")
local f = AceGUI:Create("Frame")
f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
f:SetWidth(620)
f:SetHeight(250)
f:SetTitle(L["ADDON_BETA_WARNING_TITLE"])
f:SetLayout("Flow")
-- Create a button
local btn = AceGUI:Create("Button")
btn:SetWidth(100)
btn:SetText("...")
btn:SetCallback("OnClick",
function()
if MyAddon:Disable() then
f:Hide()
end
end)
-- Add the button to the container
f:AddChild(btn)
First, MyAddon should be a local variable, especially if you are giving it that name.
Second, you are missing, or haven't posted, the other Ace3 functions. Please refer to the docs.
Third, and I'm not being picky, rather merely curious, why are you using AceGUI directly? You can, of course, but it is really designed as a backend for AceConfig widgets like dropdowns, select buttons, etc, in options tables shown through Interface/AddOns/MyAddon.
local MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyNewAddon", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceComm-3.0", "AceSerializer-3.0")
function MyAddon:OnInitialize()
-- do one-time setup here
-- such as profiles
-- or LibDataBroker support
-- ... your other code for AceGUI
btn:SetCallback("OnClick", function()
MyAddon:OnDisable() -- call this function
end)
end
function MyAddon:OnEnable()
-- register events
-- register library callbacks
-- check SVs for frames' OnShow
-- or other "on" setup
end
function MyAddon:OnDisable()
-- unregister events
-- unregister callbacks
-- hide frames or any other functionality
-- you want disabled
-- do "off" setup
end
As a note from experience, localizing text using tokens rather than strings will come back to haunt you.
If you don't touch this project for the next year or two, would you know what the following translates to?
f:SetTitle(L["ADDON_BETA_WARNING_TITLE"])
It might be better to have something like this instead. You'd gain the advantage of being able to see what the text actually is supposed to say during development, and eliminate having to contstantly look up your own translations.
f:SetTitle(L["Warning: [addon's name here] is in beta, and might not work correctly."])
The return value I obtain is true, saying the disable method worked, but the addon is still usable. Any clues?
Clues are being prevented because 90% of your post is concerned with the AceGUI frame. :-) What, exactly, do you mean by
but the addon is still usable
? What is "usable" in this context? Are there event handlers registered? (I can't tell, because you didn't show the code.) Are there slash commands that you want to stop functioning? You need to show that code. Is the frame 'f' still visible? That doesn't make sense if, as you say, "the return value I obtain is true, saying the disable method worked". How can you tell what the return value was? Are you printing it out? Then show that version of the code, please, because that's not what we're seeing.
Everything myrroddin is saying is true, and you should take it to heart. You should also show your complete code, or put it in a pastebin and link to it, and explain what you want to be "disabled" that you think isn't being disabled.
In the OnInitialize, I check if the saved variables for the profile are from an old version and if so, will offer a choice to the user. No matter what choice is given to the user, The addon must cease loading and unregister any events previously registered.
Maybe I misunderstand the use of the AceAddon:Disable() function. I presumed that this function disabled the addon entirely like when you select what addon you want enabled in the addon options on the game login screen.
After loading the profile, I check if it's from a version prior to x.x.x.x. If it is, I must inform the user and stop the addon from loading any further.
As for myrroddin's suggestion on using tokens, I guess it would make sense in the long run considering I already have over 500 strings. The only problem I find with using this method is if I decide to change the way the message is presented to the user, the token might not express the correct message. I'm used to C++ #define in caps, figured I'd use the same here. Thank you.
GJB = LibStub("AceAddon-3.0"):NewAddon("GarrisonJukeBox", "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0", "AceComm-3.0", "AceSerializer-3.0")
local L = LibStub("AceLocale-3.0"):GetLocale("GarrisonJukeBox")
_G["GJB"] = GJB -- Isn't GJB already in the global scope?
function GJB:OnInitialize()
self.db = {}
self.db = LibStub("AceDB-3.0"):New("GarrisonJukeBoxDB", self.defaults, true)
if self:VersionCheckResetData() then
self.OnInitialize = nil -- ewww, twice
GJB:Disabled()
return -- sloppy maybe but it works !!!
end
self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileChanged")
self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileChanged")
self.db.RegisterCallback(self, "OnProfileReset", "OnProfileChanged")
LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable("GarrisonJukeBoxDB", self.GenerateOptions)
LibStub("AceConfig-3.0"):RegisterOptionsTable("GarrisonJukeBox", self.slashcmd, "gjb")
local ACD3 = LibStub("AceConfigDialog-3.0")
self.optionsFrames = {}
self.optionsFrames.JukeBox = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["ADDON_NAME"], nil, "JukeBox")
self.optionsFrames.Zones = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["OPT_ZONES"], L["ADDON_NAME"], "Zones")
self.optionsFrames.Advanced = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["OPT_ADVANCED"], L["ADDON_NAME"], "Advanced")
self.optionsFrames.STMode = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["OPT_STMODE"], L["ADDON_NAME"], "STMode")
self.optionsFrames.Settings = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["OPT_SETTINGS"], L["ADDON_NAME"], "Settings")
self.optionsFrames.Audio = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["OPT_AUDIO"], L["ADDON_NAME"], "Audio")
self:RegisterModuleOptions("GJBSlashCmd", self.slashcmd, L["ADDON_SLASHCMD"])
self:RegisterModuleOptions("Profiles", function() return LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db) end, L["Profiles"])
self.optionsFrames.About = ACD3:AddToBlizOptions("GarrisonJukeBoxDB", L["OPT_ABOUT"], L["ADDON_NAME"], "About")
self.OnInitialize = nil
end
function GJB:OnEnable()
self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnPEWEvent")
self:RegisterEvent("PET_BATTLE_OPENING_START", "OnPetBattleStart")
self:RegisterEvent("PET_BATTLE_OVER", "OnPetBattleEnd")
self:RegisterEvent("ZONE_CHANGED_NEW_AREA", "OnZoneChangedNewArea")
self:RegisterEvent("ZONE_CHANGED_INDOORS", "OnZoneChangedIndoors")
self:RegisterEvent("ZONE_CHANGED", "OnZoneChanged")
self:RegisterEvent("GROUP_ROSTER_UPDATE", "OnJoinParty")
-- Register callbacks for inter-addon comms
self:RegisterComm(COMM_PREFIX .. "ReqVersion", "OnBuddyReqVersion") -- Sends requesters version and name-realm to all party members
self:RegisterComm(COMM_PREFIX .. "SentVersion", "OnBuddySentVersion") -- Sends version back to requester
self:RegisterComm(COMM_PREFIX .. "GetMusic", "OnBuddyListenMusic") -- Get music sent by leader
end
-- -------------------------------------------
-- OnDisable
-- -------------------------------------------
function GJB:OnDisable()
self:UnregisterEvent("PLAYER_ENTERING_WORLD")
self:UnregisterEvent("PET_BATTLE_OPENING_START")
self:UnregisterEvent("PET_BATTLE_OVER")
self:UnregisterEvent("ZONE_CHANGED_NEW_AREA")
self:UnregisterEvent("ZONE_CHANGED_INDOORS")
self:UnregisterEvent("ZONE_CHANGED")
self:UnregisterEvent("GROUP_ROSTER_UPDATE")
-- add comm events unregisters
end
Yes, GJB is already global in your first line. But why is it global? There is zero reason for that; make it a local variable. If you need GJB in another file within the addon, there is an Ace command for that.
Remove the first line in your OnInitialize. The db is already a table in your second line.
Speaking of your second line, nowhere do you define "self.defaults".
Your defaults should be a table in the main scope of your code, and you want an entry called "enable" or "enableAddOn", because...
To set the enabled state of your addon, after setting the DB up, add this command:
self:SetEnabledState(self.db.profile.enableAddOn)
That should eliminate the need for the self.OnInitialize variable.
In your OnDisable function, instead of unregistering each event individually, instead call
1. This will be added considering my project is split in several different files.
local GJB = LibStub("AceAddon-3.0"):GetAddon("GarrisonJukeBox")
2. I'm used to initializing variables.
3. 4. I omitted the default table but this was already in my code.
GJB.defaults = {
profile = {
AddonEnabled = true,
... other saved variables here
},
}
5. 6. Will modifying self.db.profile.AddonEnabled further on in the code automatically set the AceAddon's internal enabled variable or do I still need to call GJB : Disable()?
You will notice that is called in OnIntialize. What that does is call OnEnable if the Boolean is true, and OnDisable if false.
Behind the scenes, Ace calls Enable, which calls OnEnable, etc; Disable, which calls OnDisable. You don't need (or should, honestly) to call Enable/Disable yourself.
Therefore, you want your code that enables/disables in the OnEnable/OnDisable.
As for initializing variables like the SVs, what you did was correct for non-Ace AddOns. AceDB does the heavy lifting for you, and provides additional functionality.
Another tip about localization: after setting L as you have done, add this line:
GJB.L = L
Now, in your other files, instead of loading AceLocale all over again, at the top of each additional file that will have translatable strings, you get:
local GJB = LibStub("AceAddon-3.0"):GetAddon("GarrisonJukeBox")
local L = GJB.L
Nothing inherently wrong with using a table like GJB.defaults. You just don't need it outside of the main file scope. Meaning, you won't need to access the defaults table in any other file. You could simply make it a local file table.
Maybe I misunderstand the use of the AceAddon:Disable() function. I presumed that this function disabled the addon entirely like when you select what addon you want enabled in the addon options on the game login screen.
Ah. No, I'm afraid you have misunderstood.
AceAddon:Disable() simply provides a way to programmatically "turn off" an addon from other addons. What it does exactly is to call the "OnDisable" methods of your addon, along with any modules and embedded libraries. For example, AceEvent will stop responding to events, AceComm will stop listening to messages, and so on. What your addon does in OnDisable is up to you.
What you want to use can only be done by calling DisableAddOn, which is a global function and unrelated to Ace. (The names are unfortunately similar. The Ace library was, I think, slightly earlier than the ability to disable addons from within a running session using the API. In any case, they have nothing to do with one another.)
For example, AceEvent will stop responding to events, AceComm will stop listening to messages, and so on. What your addon does in OnDisable is up to you.
To add what Farmbuyer said, just to be clear, is that OnDisable won't do things automatically, even things related to other Ace3 libraries.
I underlined part of the quote, because as he suggested, you would have to expressly define things to turn off in OnDisable. Just as you would have to expressly register events, third party library callbacks, etc in OnEnable.
The Disable() function can be thought of working in this way. Say you have two AddOns named Steve and Henry. These could be seperate AddOns, or possibly Henry could be a module of Steve. That doesn't matter.
Now let's say you want to disable Steve, but you want Henry to disable Steve.
Ok, now I understand. I thought GJB: Disable() would put the addon in a "non functioning" state; no event triggers whatsoever except OnEnable
So I'm better off managing an enabled state of GJB myself. Only thing is, it seems redundant to check all the time for every action such as a button click or event trigger.
something like this:
if GJB.enabled then
-- Do what was called for
end
Are there facilities in place to deal with this or is this example the way to go other than managing all events in every callback?
Thank you very much for helping me understand this.
Ok, now I understand. I thought GJB: Disable() would put the addon in a "non functioning" state; no event triggers whatsoever except OnEnable
Well, only the "event triggers" that Ace3 knows about.
The things you register with, for example, AceEvent will be non functioning. But :Disable has no way of knowing which frames to hide or which buttons to ignore. That's what your OnDisable has to do: hide frames, disable buttons, whatever.
Try that, then post that version of your code.
Rollback Post to RevisionRollBack
To post a comment, please login or register a new account.
I'm trying to disable my addon without success. Am I missing something?
only hides the AceGUI frame 'f'. What I want to achieve is to disable the whole addon 'MyAddon' previously created with
The return value I obtain is true, saying the disable method worked, but the addon is still usable. Any clues?
Second, you are missing, or haven't posted, the other Ace3 functions. Please refer to the docs.
Third, and I'm not being picky, rather merely curious, why are you using AceGUI directly? You can, of course, but it is really designed as a backend for AceConfig widgets like dropdowns, select buttons, etc, in options tables shown through Interface/AddOns/MyAddon.
If you don't touch this project for the next year or two, would you know what the following translates to?
It might be better to have something like this instead. You'd gain the advantage of being able to see what the text actually is supposed to say during development, and eliminate having to contstantly look up your own translations.
Also, wherever possible, use Blizzard's global strings. They are automatically translated.
Clues are being prevented because 90% of your post is concerned with the AceGUI frame. :-) What, exactly, do you mean by
? What is "usable" in this context? Are there event handlers registered? (I can't tell, because you didn't show the code.) Are there slash commands that you want to stop functioning? You need to show that code. Is the frame 'f' still visible? That doesn't make sense if, as you say, "the return value I obtain is true, saying the disable method worked". How can you tell what the return value was? Are you printing it out? Then show that version of the code, please, because that's not what we're seeing.
Everything myrroddin is saying is true, and you should take it to heart. You should also show your complete code, or put it in a pastebin and link to it, and explain what you want to be "disabled" that you think isn't being disabled.
Maybe I misunderstand the use of the AceAddon:Disable() function. I presumed that this function disabled the addon entirely like when you select what addon you want enabled in the addon options on the game login screen.
After loading the profile, I check if it's from a version prior to x.x.x.x. If it is, I must inform the user and stop the addon from loading any further.
As for myrroddin's suggestion on using tokens, I guess it would make sense in the long run considering I already have over 500 strings. The only problem I find with using this method is if I decide to change the way the message is presented to the user, the token might not express the correct message. I'm used to C++ #define in caps, figured I'd use the same here. Thank you.
1. This will be added considering my project is split in several different files.
2. I'm used to initializing variables.
3. 4. I omitted the default table but this was already in my code.
5. 6. Will modifying self.db.profile.AddonEnabled further on in the code automatically set the AceAddon's internal enabled variable or do I still need to call GJB : Disable()?
7. Got it.
Thanks
Behind the scenes, Ace calls Enable, which calls OnEnable, etc; Disable, which calls OnDisable. You don't need (or should, honestly) to call Enable/Disable yourself.
Therefore, you want your code that enables/disables in the OnEnable/OnDisable.
As for initializing variables like the SVs, what you did was correct for non-Ace AddOns. AceDB does the heavy lifting for you, and provides additional functionality.
Another tip about localization: after setting L as you have done, add this line:
Now, in your other files, instead of loading AceLocale all over again, at the top of each additional file that will have translatable strings, you get:
Nothing inherently wrong with using a table like GJB.defaults. You just don't need it outside of the main file scope. Meaning, you won't need to access the defaults table in any other file. You could simply make it a local file table.
Ah. No, I'm afraid you have misunderstood.
AceAddon:Disable() simply provides a way to programmatically "turn off" an addon from other addons. What it does exactly is to call the "OnDisable" methods of your addon, along with any modules and embedded libraries. For example, AceEvent will stop responding to events, AceComm will stop listening to messages, and so on. What your addon does in OnDisable is up to you.
What you want to use can only be done by calling DisableAddOn, which is a global function and unrelated to Ace. (The names are unfortunately similar. The Ace library was, I think, slightly earlier than the ability to disable addons from within a running session using the API. In any case, they have nothing to do with one another.)
To add what Farmbuyer said, just to be clear, is that OnDisable won't do things automatically, even things related to other Ace3 libraries.
I underlined part of the quote, because as he suggested, you would have to expressly define things to turn off in OnDisable. Just as you would have to expressly register events, third party library callbacks, etc in OnEnable.
The Disable() function can be thought of working in this way. Say you have two AddOns named Steve and Henry. These could be seperate AddOns, or possibly Henry could be a module of Steve. That doesn't matter.
Now let's say you want to disable Steve, but you want Henry to disable Steve.
In Henry.lua, you would call
Which would call, in Steve.lua,
So I'm better off managing an enabled state of GJB myself. Only thing is, it seems redundant to check all the time for every action such as a button click or event trigger.
something like this:
Are there facilities in place to deal with this or is this example the way to go other than managing all events in every callback?
Thank you very much for helping me understand this.
Well, only the "event triggers" that Ace3 knows about.
The things you register with, for example, AceEvent will be non functioning. But :Disable has no way of knowing which frames to hide or which buttons to ignore. That's what your OnDisable has to do: hide frames, disable buttons, whatever.
Try that, then post that version of your code.