commit c2ced38961f17541bbe1168d5b1ed55689c4206b Author: Florian Brinker Date: Mon Oct 10 21:48:47 2016 +0200 init diff --git a/AchievementInfo.lua b/AchievementInfo.lua new file mode 100644 index 0000000..e5cf84e --- /dev/null +++ b/AchievementInfo.lua @@ -0,0 +1,41 @@ +--[[ + AchievementInfo + @author Asto, @Astarax +]] + + + +-- Init +AchievementInfo.hijackedFirstLoad = false +function AchievementInfo.initialize(eventCode, addOnName) + if (addOnName ~= AchievementInfo.name) then return end + + -- + if AchievementInfo.hijackedFirstLoad == false then + AchievementInfo.hijackedFirstLoad = true + + -- Load Saved Variables + AchievementInfo.savedVars = AchievementInfo.loadSavedVars() + + -- Load Language Data + LANG = AchievementInfo.loadLanguage() + + -- Settings Panel + AchievementInfo.createSettingsPanel() + + -- Register Events + AchievementInfo.registerEvent(EVENT_ACHIEVEMENT_UPDATED, AchievementInfo.onAchievementUpdated) + end + + -- Status Output (debug mode only) + if AchievementInfo.settingGet("devDebug") then + zo_callLater(function() + AchievementInfo.echo(AchievementInfo.name .. " initialized in DEBUG MODE :)") + end, 500) + end +end + + + +-- Register the Init Event +AchievementInfo.registerEvent(EVENT_ADD_ON_LOADED, AchievementInfo.initialize) diff --git a/AchievementInfo.txt b/AchievementInfo.txt new file mode 100644 index 0000000..12d946e --- /dev/null +++ b/AchievementInfo.txt @@ -0,0 +1,23 @@ +## Title: AchievementInfo +## Description: Displays progress updates for achievements in the chat. You can display update-details like 'Slay 25/100 Monsters' if interested. |cFF0000Important:|r The messages appear only when an achievement is updated. This is no permanent tracking tool. +## Version: 2.10 +## Author: |c87B7CCAsto|r, @Astarax +## Contact: mail@coded-with-heart.com + +## APIVersion: 100016 +## SavedVariables: ACHIEVEMENT_INFO_DB + +## OptionalDependsOn: LibAddonMenu-2.0, LibStub + +## Libraries: +Libs/LibStub/LibStub.lua +Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua + +## Helper: +AchievementInfoCommon.lua +AchievementInfoLangStore.lua + +## Core: +AchievementInfo.lua +AchievementInfoSettings.lua +AchievementInfoApplication.lua diff --git a/AchievementInfoApplication.lua b/AchievementInfoApplication.lua new file mode 100644 index 0000000..0b610b5 --- /dev/null +++ b/AchievementInfoApplication.lua @@ -0,0 +1,193 @@ +--[[ + AchievementInfo + @author Asto, @Astarax +]] + + + +-- Do the magic +function AchievementInfo.onAchievementUpdated(eventCode, achId) + local output = "" + + -- addOn enabled? + if AchievementInfo.settingGet("genEnabled") == false then + if AchievementInfo.settingGet("devDebug") == false then + return + else + output = output .. "DEBUG (AddOn Disabled): " + end + end + -- + + local categoryId = AchievementInfo.getCorrectAchievementCategoryId(achId) + + -- ignore unwanted achievements ... + if categoryId == false then + if AchievementInfo.settingGet("devDebug") == false then + return + else + output = output .. "DEBUG (Not Next): " + end + end + -- + + -- achievement category enabled? + if categoryId ~= false and AchievementInfo.settingGet("cat"..categoryId) == false then + if AchievementInfo.settingGet("devDebug") == false then + return + else + output = output .. "DEBUG (Category Off): " + end + end + -- + + -- okay continue with the message + local detailOutput = {} + local detailOutputCount = 1 + local percentageCmpSum = 0 + local percentageReqSum = 0 + local percentageStep = false + local percentageStepSize = AchievementInfo.settingGet("genShowUpdateSteps") + + local link = GetAchievementLink(achId, LINK_STYLE_BRACKET) + local name, description = GetAchievementInfo(achId) + local catName = "/" + + if categoryId ~= false then + catName = GetAchievementCategoryInfo(categoryId) + end + + output = output .. "" .. link .. " (" .. catName .. ")" + + local numCriteria = GetAchievementNumCriteria(achId) + for i = 1, numCriteria, 1 do + local name, numCompleted, numRequired = GetAchievementCriterion(achId, i) + local tmpOutput = "" + + if i > 1 and AchievementInfo.settingGet("genOnePerLine") == false then + tmpOutput = tmpOutput .. ", " + end + + tmpOutput = tmpOutput .. name .. " " + tmpOutput = tmpOutput .. AchievementInfo.calcCriteriaColor(numCompleted, numRequired) .. numCompleted .. "|r" + tmpOutput = tmpOutput .. AchievementInfo.clrDefault .. "/" .. "|r" + tmpOutput = tmpOutput .. AchievementInfo.clrCriteriaComplete .. numRequired .. "|r" + tmpOutput = tmpOutput .. AchievementInfo.clrDefault + + if AchievementInfo.settingGet("genShowOpenDetailsOnly") == true and numCompleted ~= numRequired then + detailOutput[detailOutputCount] = tmpOutput + detailOutputCount = detailOutputCount + 1 + end + + -- show the achievement on every special achievement because it's a rare event + if numRequired == 1 and numCompleted == 1 then + percentageStep = true + -- collect the numbers to calculate the correct percentage + else + percentageReqSum = percentageReqSum + numRequired + percentageCmpSum = percentageCmpSum + numCompleted + end + end + + if percentageStep == false then + -- show at percent value + local percentage = 100 / percentageReqSum * percentageCmpSum + local percentageNext = 100 / percentageReqSum * (percentageCmpSum + 1) + + -- if percentage of percentageStepSize is hit or the value is next to it and the next value will be higher + if --[[percentage > 0 and]] percentage % percentageStepSize == 0 or (percentage % percentageStepSize > percentageNext % percentageStepSize and percentageNext % percentageStepSize ~= 0) then + percentageStep = true + -- show if this is the first numCompleted value + elseif percentageCmpSum == 1 then + percentageStep = true + end + end + + -- show details? + local detailsCount = AchievementInfo.tableLength(detailOutput) + if AchievementInfo.settingGet("genShowDetails") == true and detailsCount > 0 and AchievementInfo.settingGet("genOnePerLine") == false then + output = output .. " - " + + for i = 1, detailsCount, 1 do + output = output .. detailOutput[i] + end + else + output = output .. "." + end + -- + + -- output on every step OR when its a defined percentage step + if AchievementInfo.settingGet("genShowEveryUpdate") == false and percentageStep == false then + if AchievementInfo.settingGet("devDebug") == false then + return + else + output = "DEBUG (" .. AchievementInfo.settingGet("genShowUpdateSteps") .. "% Rule): " .. output + end + end + -- + + -- + if percentageReqSum == percentageCmpSum then + output = LANG.Completed .. ": " .. output + else + output = LANG.Updated .. ": " .. output + end + -- + + AchievementInfo.echo(output) + + -- output the details line by line - start @2 because the normal output happend before (achievement name) + if AchievementInfo.settingGet("genShowDetails") == true and AchievementInfo.settingGet("genOnePerLine") == true then + for i = 1, AchievementInfo.tableLength(detailOutput), 1 do + AchievementInfo.echo(detailOutput[i]) + end + end +end + + + +-- Check if the category of an achievement is valid (reverse check) +function AchievementInfo.checkForValidCategory(achId) + local categoryTopLevelIndex, categoryIndex, achievementIndex = GetCategoryInfoFromAchievementId(achId) + local reverseAchievementId = GetAchievementId(categoryTopLevelIndex, categoryIndex, achievementIndex) + + if achId == reverseAchievementId then + return true + end + + return false +end + + + +-- Get the correct achievement category +function AchievementInfo.getCorrectAchievementCategoryId(achId) + local previousAchievementId = GetPreviousAchievementInLine(achId) + local categoryId = 0 + + if AchievementInfo.checkForValidCategory(achId) == false and previousAchievementId ~= 0 then + return AchievementInfo.getCorrectAchievementCategoryId(previousAchievementId) + elseif AchievementInfo.checkForValidCategory(achId) then + categoryId = GetCategoryInfoFromAchievementId(achId) + return categoryId + else + return false + end +end + + + +-- Calculates the percentage of the achievement completition to define the color +function AchievementInfo.calcCriteriaColor(completed, required) + local percentage = 100 / required * completed + + if completed == required then + return AchievementInfo.clrCriteriaComplete + elseif percentage <= 33 then + return AchievementInfo.clrCriteriaFar + elseif percentage <= 66 then + return AchievementInfo.clrCriteriaMedi + else + return AchievementInfo.clrCriteriaClose + end +end diff --git a/AchievementInfoCommon.lua b/AchievementInfoCommon.lua new file mode 100644 index 0000000..648862c --- /dev/null +++ b/AchievementInfoCommon.lua @@ -0,0 +1,65 @@ +--[[ + AchievementInfo + @author Asto, @Astarax +]] + + + +AchievementInfo = {} +AchievementInfo.name = "AchievementInfo" +AchievementInfo.author = "Asto, @Astarax" +AchievementInfo.version = 2.10 +AchievementInfo.savedVars = nil +AchievementInfo.LangStore = {} + +local clrPrefix = "|c" +AchievementInfo.clrDefault = clrPrefix .. "87B7CC" +AchievementInfo.clrCriteriaFar = clrPrefix .. "F27C7C" +AchievementInfo.clrCriteriaMedi = clrPrefix .. "EDE858" +AchievementInfo.clrCriteriaClose = clrPrefix .. "CCF048" +AchievementInfo.clrCriteriaComplete = clrPrefix .. "71DE73" +AchievementInfo.clrSettingsHeader = clrPrefix .. "F0C91A" + + + + +-- Load the correct language +function AchievementInfo.loadLanguage() + local lang = GetCVar("language.2") + + if lang == "de" then + return LANG_STORE.DE + elseif lang == "fr" then + return LANG_STORE.FR + else + return LANG_STORE.EN + end +end + + + +-- Event Registration Shortcut +function AchievementInfo.registerEvent(event, handler) + EVENT_MANAGER:RegisterForEvent(AchievementInfo.name, event, handler) +end + + + +-- Message Output Shortcut +function AchievementInfo.echo(message) + -- addOn enabled? + if AchievementInfo.settingGet("genEnabled") == false then return end + + if message ~= nil then + CHAT_SYSTEM:AddMessage(AchievementInfo.clrDefault..message.."|r") + end +end + + + +-- Helper method to count a lua table +function AchievementInfo.tableLength(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end diff --git a/AchievementInfoLangStore.lua b/AchievementInfoLangStore.lua new file mode 100644 index 0000000..5dafece --- /dev/null +++ b/AchievementInfoLangStore.lua @@ -0,0 +1,165 @@ +--[[ + AchievementInfo + @author Asto, @Astarax +]] + + + +LANG_STORE = {} +LANG_STORE.EN = {} -- English version by Asto +LANG_STORE.DE = {} -- German version by Asto (native) +LANG_STORE.FR = {} -- French version open + + + +--[[ + English version + @author Asto +]] +-- AddOn Output +LANG_STORE.EN.Updated = "Updated" +LANG_STORE.EN.Completed = "Completed" + +-- AddOn Settings Header +LANG_STORE.EN.SettingsHeader = {} +LANG_STORE.EN.SettingsHeader.General = "General" + +LANG_STORE.EN.SettingsHeader.Categories = "Categories" +LANG_STORE.EN.SettingsHeader.CategoriesDescription = "Here you can setup the notifications per category" + +LANG_STORE.EN.SettingsHeader.Development = "Development" + +-- AddOn Settings General Options +LANG_STORE.EN.SettingsOption = {} +LANG_STORE.EN.SettingsOption.AddOnEnabled = "AddOn enabled" +LANG_STORE.EN.SettingsOption.AddOnEnabledTooltip = "Enable or disable this AddOn" +LANG_STORE.EN.SettingsOption.AddOnEnabledWarning = "Only the output messages can be disabled here" + +LANG_STORE.EN.SettingsOption.ShowEveryUpdate = "Show every update" +LANG_STORE.EN.SettingsOption.ShowEveryUpdateTooltip = "Shows a message on every status update of an achievement. Otherwise the messages appear only in steps of x%" + +LANG_STORE.EN.SettingsOption.ShowUpdateSteps = "Notification steps (%)" +LANG_STORE.EN.SettingsOption.ShowUpdateStepsTooltip = "Defines the step width of notifications, if '" .. LANG_STORE.EN.SettingsOption.ShowEveryUpdate .. "' is disabled" + +LANG_STORE.EN.SettingsOption.ShowDetails = "Show details" +LANG_STORE.EN.SettingsOption.ShowDetailsTooltip = "Shows progress details in each update message" + +LANG_STORE.EN.SettingsOption.ShowOpenDetailsOnly = "Show incomplete details only" +LANG_STORE.EN.SettingsOption.ShowOpenDetailsOnlyTooltip = "Shows only the incomplete tasks of an achiemevent in the details" + +-- pCHat compatibility option +LANG_STORE.EN.SettingsOption.OneElementPerLine = "Output line by line" +LANG_STORE.EN.SettingsOption.OneElementPerLineTooltip = "Shows each part of an achievement in a single line" +LANG_STORE.EN.SettingsOption.OneElementPerLineWarning = "Necessary for pChat compatibility" + +-- AddOn Settings Category Options +-- The categories are taken from the game language files +LANG_STORE.EN.SettingsOption.CategoryTooltip = "Show messages for the category" + +-- AddOn Settings Development Options +LANG_STORE.EN.SettingsOption.DebugMode = "Debug Mode" +LANG_STORE.EN.SettingsOption.DebugModeTooltip = "Shows hidden messages to check if they are hidden by mistake" +LANG_STORE.EN.SettingsOption.DebugModeWarning = "In most cases you don't need to activate this option" + + + +--[[ + German version + @author Asto +]] +-- AddOn Output +LANG_STORE.DE.Updated = "Aktualisiert" +LANG_STORE.DE.Completed = "Abgeschlossen" + +-- AddOn Settings Header +LANG_STORE.DE.SettingsHeader = {} +LANG_STORE.DE.SettingsHeader.General = "Allgemein" + +LANG_STORE.DE.SettingsHeader.Categories = "Kategorien" +LANG_STORE.DE.SettingsHeader.CategoriesDescription = "Hier können die Benachrichtigungen je Kategorie eingestellt werden" + +LANG_STORE.DE.SettingsHeader.Development = "Entwicklung" + +-- AddOn Settings General Options +LANG_STORE.DE.SettingsOption = {} +LANG_STORE.DE.SettingsOption.AddOnEnabled = "AddOn aktiviert" +LANG_STORE.DE.SettingsOption.AddOnEnabledTooltip = "Aktiviere oder deaktiviere dieses AddOn" +LANG_STORE.DE.SettingsOption.AddOnEnabledWarning = "An dieser Stelle können nur die Ausgaben deaktiviert werden." + +LANG_STORE.DE.SettingsOption.ShowEveryUpdate = "Zeige alle Fortschritte" +LANG_STORE.DE.SettingsOption.ShowEveryUpdateTooltip = "Zeigt bei jeder Aktualisierung eines Erfolgs einen Hinweis. Alternativ wird nur in x% Schritten ein Status ausgegeben" + +LANG_STORE.DE.SettingsOption.ShowUpdateSteps = "Benachrichtigungsschritte (%)" +LANG_STORE.DE.SettingsOption.ShowUpdateStepsTooltip = "Definiert die Schrittweite (Häufigkeit) der Benachrichtigungen, wenn '" .. LANG_STORE.DE.SettingsOption.ShowEveryUpdate .. "' deaktiviert ist" + +LANG_STORE.DE.SettingsOption.ShowDetails = "Zeige Details" +LANG_STORE.DE.SettingsOption.ShowDetailsTooltip = "Zeigt die Fortschritt-Details des Erfolgs im Hinweis an" + +LANG_STORE.DE.SettingsOption.ShowOpenDetailsOnly = "Zeige nur unerledigte Details" +LANG_STORE.DE.SettingsOption.ShowOpenDetailsOnlyTooltip = "Zeigt nur offene Aufgaben in den Fortschritt-Details an" + +-- pCHat compatibility option +LANG_STORE.DE.SettingsOption.OneElementPerLine = "Zeilenweise Ausgabe" +LANG_STORE.DE.SettingsOption.OneElementPerLineTooltip = "Zeigt jeden Unterpunkt eines Erfolgs als eigene Zeile im Chat" +LANG_STORE.DE.SettingsOption.OneElementPerLineWarning = "Voraussetzung für eine pChat Kompatibilität" + +-- AddOn Settings Category Options +-- The categories are taken from the game language files +LANG_STORE.DE.SettingsOption.CategoryTooltip = "Zeige Nachrichten für die Kategorie" + +-- AddOn Settings Development Options +LANG_STORE.DE.SettingsOption.DebugMode = "Debug Modus" +LANG_STORE.DE.SettingsOption.DebugModeTooltip = "Zeigt versteckte Nachrichten, um zu prüfen ob ggf. Nachrichten unrechtmäßig unterdrückt werden" +LANG_STORE.DE.SettingsOption.DebugModeWarning = "In den meißten Fällen muss diese Option nicht aktiviert werden" + + + + +--[[ + French version + @author Llwydd +]] +-- AddOn Output +LANG_STORE.FR.Updated = "Mis à jour" +LANG_STORE.FR.Completed = "Terminé" + +-- AddOn Settings Header +LANG_STORE.FR.SettingsHeader = {} +LANG_STORE.FR.SettingsHeader.General = "Général" + +LANG_STORE.FR.SettingsHeader.Categories = "Catégories" +LANG_STORE.FR.SettingsHeader.CategoriesDescription = "Ici vous pouvez gérer les notifications par catégories" + +LANG_STORE.FR.SettingsHeader.Development = "Développement" + +-- AddOn Settings General Options +LANG_STORE.FR.SettingsOption = {} +LANG_STORE.FR.SettingsOption.AddOnEnabled = "Extension activée" +LANG_STORE.FR.SettingsOption.AddOnEnabledTooltip = "Active ou désactive cette extension" +LANG_STORE.FR.SettingsOption.AddOnEnabledWarning = "Seul les messages sortants peuvent être désactivés" + +LANG_STORE.FR.SettingsOption.ShowEveryUpdate = "Affichage de chaque mise à jour" +LANG_STORE.FR.SettingsOption.ShowEveryUpdateTooltip = "Affiche un message pour chaque mise à jour d'un succès. Le reste du temps les messages n'apparaissent que sous forme de plage de x%" + +LANG_STORE.FR.SettingsOption.ShowUpdateSteps = "Etapes de notification (%)" +LANG_STORE.FR.SettingsOption.ShowUpdateStepsTooltip = "Définie la plage des notifications, si '" .. LANG_STORE.FR.SettingsOption.ShowEveryUpdate .. "' est désactivé" + +LANG_STORE.FR.SettingsOption.ShowDetails = "Affichage des détails" +LANG_STORE.FR.SettingsOption.ShowDetailsTooltip = "Affiche les détails de progression de chaque message de mise à jour" + +LANG_STORE.FR.SettingsOption.ShowOpenDetailsOnly = "Affichage des détails incomplets" +LANG_STORE.FR.SettingsOption.ShowOpenDetailsOnlyTooltip = "Affiche, dans les détails, uniquement les taches incomplètes d'un succès" + +-- pCHat compatibility option +LANG_STORE.FR.SettingsOption.OneElementPerLine = "Sortie ligne par ligne" +LANG_STORE.FR.SettingsOption.OneElementPerLineTooltip = "Affiche chaque partie d'un succès sur une simple ligne" +LANG_STORE.FR.SettingsOption.OneElementPerLineWarning = "Nécessite la compatibilité avec pChat" + +-- AddOn Settings Category Options +-- The categories are taken from the game language files +LANG_STORE.FR.SettingsOption.CategoryTooltip = "Affiche les messages pour la catégorie" + +-- AddOn Settings Development Options +LANG_STORE.FR.SettingsOption.DebugMode = "Mode de débogage" +LANG_STORE.FR.SettingsOption.DebugModeTooltip = "Affiche les messages cachés pour vérifier s'ils n'ont pas été cachés par erreur" +LANG_STORE.FR.SettingsOption.DebugModeWarning = "Dans la majorité des cas vous n'avez pas besoin d'activer cette option" \ No newline at end of file diff --git a/AchievementInfoSettings.lua b/AchievementInfoSettings.lua new file mode 100644 index 0000000..7c594e9 --- /dev/null +++ b/AchievementInfoSettings.lua @@ -0,0 +1,175 @@ +--[[ + AchievementInfo + @author Asto, @Astarax +]] + + + +-- Function to set and load the Saved Variables +function AchievementInfo.loadSavedVars() + local defaults = { + genEnabled = true, + genShowEveryUpdate = true, + genShowUpdateSteps = 25, + genShowDetails = false, + genShowOpenDetailsOnly = true, + genOnePerLine = true, + cat1 = true, + cat2 = true, + cat3 = true, + cat4 = true, + cat5 = true, + cat6 = true, + cat7 = true, + cat8 = true, + cat9 = true, + cat10 = true, + devDebug = false + } + + return ZO_SavedVars:New("ACHIEVEMENT_INFO_DB", 1, nil, defaults) +end + + + +-- Function to create the settings panel +function AchievementInfo.createSettingsPanel() + local LAM = LibStub("LibAddonMenu-2.0") + + local panelData = { + type = "panel", + name = AchievementInfo.name, + displayName = AchievementInfo.clrDefault..AchievementInfo.name, + author = AchievementInfo.author, + version = string.format("%.1f", AchievementInfo.version), + slashCommand = "/achievementInfo" + } + + local optionsTable = { + [1] = { + type = "header", + name = AchievementInfo.clrSettingsHeader..LANG.SettingsHeader.General + }, + [2] = { + type = "checkbox", + name = LANG.SettingsOption.AddOnEnabled, + tooltip = LANG.SettingsOption.AddOnEnabledTooltip, + getFunc = function() return AchievementInfo.settingGet("genEnabled") end, + setFunc = function() AchievementInfo.settingToogle("genEnabled") end, + warning = LANG.SettingsOption.AddOnEnabledWarning + }, + [3] = { + type = "checkbox", + name = LANG.SettingsOption.ShowEveryUpdate, + tooltip = LANG.SettingsOption.ShowEveryUpdateTooltip, + getFunc = function() return AchievementInfo.settingGet("genShowEveryUpdate") end, + setFunc = function() AchievementInfo.settingToogle("genShowEveryUpdate") end + }, + [4] = { + type = "slider", + name = LANG.SettingsOption.ShowUpdateSteps, + tooltip = LANG.SettingsOption.ShowUpdateStepsTooltip, + min = 1, + max = 100, + step = 1, + getFunc = function() return AchievementInfo.settingGet("genShowUpdateSteps") end, + setFunc = function(value) AchievementInfo.settingSet("genShowUpdateSteps", value) end, + default = 25 + }, + [5] = { + type = "checkbox", + name = LANG.SettingsOption.ShowDetails, + tooltip = LANG.SettingsOption.ShowDetailsTooltip, + getFunc = function() return AchievementInfo.settingGet("genShowDetails") end, + setFunc = function() AchievementInfo.settingToogle("genShowDetails") end + }, + [6] = { + type = "checkbox", + name = LANG.SettingsOption.ShowOpenDetailsOnly, + tooltip = LANG.SettingsOption.ShowOpenDetailsOnlyTooltip, + getFunc = function() return AchievementInfo.settingGet("genShowOpenDetailsOnly") end, + setFunc = function() AchievementInfo.settingToogle("genShowOpenDetailsOnly") end + }, + [7] = { + type = "checkbox", + name = LANG.SettingsOption.OneElementPerLine, + tooltip = LANG.SettingsOption.OneElementPerLineTooltip, + getFunc = function() return AchievementInfo.settingGet("genOnePerLine") end, + setFunc = function() AchievementInfo.settingToogle("genOnePerLine") end, + warning = LANG.SettingsOption.OneElementPerLineWarning + }, + [8] = { + type = "header", + name = AchievementInfo.clrSettingsHeader .. LANG.SettingsHeader.Categories + }, + [9] = { + type = "description", + text = LANG.SettingsHeader.CategoriesDescription .. ":" + } + } + + -- Add categories dynamically + local numCats = GetNumAchievementCategories() + local catCount = 1 + + for i = 1, numCats, 1 do + catName, numSubCats = GetAchievementCategoryInfo(i) + + table.insert(optionsTable, { + type = "checkbox", + name = catName, + tooltip = LANG.SettingsOption.CategoryTooltip .. " '" .. catName .. "'", + getFunc = function() return AchievementInfo.settingGet("cat"..i) end, + setFunc = function() AchievementInfo.settingToogle("cat"..i) end + }) + end + + -- Debug setting at the end + table.insert(optionsTable, { + type = "header", + name = AchievementInfo.clrSettingsHeader .. LANG.SettingsHeader.Development + }) + + table.insert(optionsTable, { + type = "checkbox", + name = LANG.SettingsOption.DebugMode, + tooltip = LANG.SettingsOption.DebugModeTooltip, + getFunc = function() return AchievementInfo.settingGet("devDebug") end, + setFunc = function() AchievementInfo.settingToogle("devDebug") end, + warning = LANG.SettingsOption.DebugModeWarning + }) + + -- Register + LAM:RegisterAddonPanel(AchievementInfo.name.."SettingsPanel", panelData) + LAM:RegisterOptionControls(AchievementInfo.name.."SettingsPanel", optionsTable) +end + + + +-- Function to retrieve settings +function AchievementInfo.settingGet(id) + if AchievementInfo.savedVars[id] == nil then + AchievementInfo.savedVars[id] = false + return false + else + return AchievementInfo.savedVars[id] + end +end + + + +-- Function to toggle Checkbox values +function AchievementInfo.settingToogle(id) + if AchievementInfo.savedVars[id] == false then + AchievementInfo.savedVars[id] = true + else + AchievementInfo.savedVars[id] = false + end +end + + + +-- Function to set settings +function AchievementInfo.settingSet(id, value) + AchievementInfo.savedVars[id] = value +end diff --git a/Libs/LibAddonMenu-2.0.txt b/Libs/LibAddonMenu-2.0.txt new file mode 100644 index 0000000..c6c4796 --- /dev/null +++ b/Libs/LibAddonMenu-2.0.txt @@ -0,0 +1,25 @@ +## APIVersion: 100011 +## Title: LibAddonMenu-2.0 +## Version: 2.0 r18 +## Author: Seerah, sirinsidiator, et al. +## Contributors: votan, merlight, Garkin +## Description: A library to aid in the creation of option panels. + + +LibStub\LibStub.lua + +LibAddonMenu-2.0\LibAddonMenu-2.0.lua + +LibAddonMenu-2.0\controls\panel.lua +LibAddonMenu-2.0\controls\submenu.lua +LibAddonMenu-2.0\controls\button.lua +LibAddonMenu-2.0\controls\checkbox.lua +LibAddonMenu-2.0\controls\colorpicker.lua +LibAddonMenu-2.0\controls\custom.lua +LibAddonMenu-2.0\controls\description.lua +LibAddonMenu-2.0\controls\dropdown.lua +LibAddonMenu-2.0\controls\editbox.lua +LibAddonMenu-2.0\controls\header.lua +LibAddonMenu-2.0\controls\slider.lua +LibAddonMenu-2.0\controls\texture.lua +LibAddonMenu-2.0\controls\iconpicker.lua diff --git a/Libs/LibAddonMenu-2.0/LICENSE b/Libs/LibAddonMenu-2.0/LICENSE new file mode 100644 index 0000000..b862858 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/LICENSE @@ -0,0 +1,201 @@ + The Artistic License 2.0 + + Copyright (c) 2015 Ryan Lakanen (Seerah) + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble + +This license establishes the terms under which a given free software +Package may be copied, modified, distributed, and/or redistributed. +The intent is that the Copyright Holder maintains some artistic +control over the development of that Package while still keeping the +Package available as open source and free software. + +You are always permitted to make arrangements wholly outside of this +license directly with the Copyright Holder of a given Package. If the +terms of this license do not permit the full use that you propose to +make of the Package, you should contact the Copyright Holder and seek +a different licensing arrangement. + +Definitions + + "Copyright Holder" means the individual(s) or organization(s) + named in the copyright notice for the entire Package. + + "Contributor" means any party that has contributed code or other + material to the Package, in accordance with the Copyright Holder's + procedures. + + "You" and "your" means any person who would like to copy, + distribute, or modify the Package. + + "Package" means the collection of files distributed by the + Copyright Holder, and derivatives of that collection and/or of + those files. A given Package may consist of either the Standard + Version, or a Modified Version. + + "Distribute" means providing a copy of the Package or making it + accessible to anyone else, or in the case of a company or + organization, to others outside of your company or organization. + + "Distributor Fee" means any fee that you charge for Distributing + this Package or providing support for this Package to another + party. It does not mean licensing fees. + + "Standard Version" refers to the Package if it has not been + modified, or has been modified only in ways explicitly requested + by the Copyright Holder. + + "Modified Version" means the Package, if it has been changed, and + such changes were not explicitly requested by the Copyright + Holder. + + "Original License" means this Artistic License as Distributed with + the Standard Version of the Package, in its current version or as + it may be modified by The Perl Foundation in the future. + + "Source" form means the source code, documentation source, and + configuration files for the Package. + + "Compiled" form means the compiled bytecode, object code, binary, + or any other form resulting from mechanical transformation or + translation of the Source form. + + +Permission for Use and Modification Without Distribution + +(1) You are permitted to use the Standard Version and create and use +Modified Versions for any purpose without restriction, provided that +you do not Distribute the Modified Version. + + +Permissions for Redistribution of the Standard Version + +(2) You may Distribute verbatim copies of the Source form of the +Standard Version of this Package in any medium without restriction, +either gratis or for a Distributor Fee, provided that you duplicate +all of the original copyright notices and associated disclaimers. At +your discretion, such verbatim copies may or may not include a +Compiled form of the Package. + +(3) You may apply any bug fixes, portability changes, and other +modifications made available from the Copyright Holder. The resulting +Package will still be considered the Standard Version, and as such +will be subject to the Original License. + + +Distribution of Modified Versions of the Package as Source + +(4) You may Distribute your Modified Version as Source (either gratis +or for a Distributor Fee, and with or without a Compiled form of the +Modified Version) provided that you clearly document how it differs +from the Standard Version, including, but not limited to, documenting +any non-standard features, executables, or modules, and provided that +you do at least ONE of the following: + + (a) make the Modified Version available to the Copyright Holder + of the Standard Version, under the Original License, so that the + Copyright Holder may include your modifications in the Standard + Version. + + (b) ensure that installation of your Modified Version does not + prevent the user installing or running the Standard Version. In + addition, the Modified Version must bear a name that is different + from the name of the Standard Version. + + (c) allow anyone who receives a copy of the Modified Version to + make the Source form of the Modified Version available to others + under + + (i) the Original License or + + (ii) a license that permits the licensee to freely copy, + modify and redistribute the Modified Version using the same + licensing terms that apply to the copy that the licensee + received, and requires that the Source form of the Modified + Version, and of any works derived from it, be made freely + available in that license fees are prohibited but Distributor + Fees are allowed. + + +Distribution of Compiled Forms of the Standard Version +or Modified Versions without the Source + +(5) You may Distribute Compiled forms of the Standard Version without +the Source, provided that you include complete instructions on how to +get the Source of the Standard Version. Such instructions must be +valid at the time of your distribution. If these instructions, at any +time while you are carrying out such distribution, become invalid, you +must provide new instructions on demand or cease further distribution. +If you provide valid instructions or cease distribution within thirty +days after you become aware that the instructions are invalid, then +you do not forfeit any of your rights under this license. + +(6) You may Distribute a Modified Version in Compiled form without +the Source, provided that you comply with Section 4 with respect to +the Source of the Modified Version. + + +Aggregating or Linking the Package + +(7) You may aggregate the Package (either the Standard Version or +Modified Version) with other packages and Distribute the resulting +aggregation provided that you do not charge a licensing fee for the +Package. Distributor Fees are permitted, and licensing fees for other +components in the aggregation are permitted. The terms of this license +apply to the use and Distribution of the Standard or Modified Versions +as included in the aggregation. + +(8) You are permitted to link Modified and Standard Versions with +other works, to embed the Package in a larger work of your own, or to +build stand-alone binary or bytecode versions of applications that +include the Package, and Distribute the result without restriction, +provided the result does not expose a direct interface to the Package. + + +Items That are Not Considered Part of a Modified Version + +(9) Works (including, but not limited to, modules and scripts) that +merely extend or make use of the Package, do not, by themselves, cause +the Package to be a Modified Version. In addition, such works are not +considered parts of the Package itself, and are not subject to the +terms of this license. + + +General Provisions + +(10) Any use, modification, and distribution of the Standard or +Modified Versions is governed by this Artistic License. By using, +modifying or distributing the Package, you accept this license. Do not +use, modify, or distribute the Package, if you do not accept this +license. + +(11) If your Modified Version has been derived from a Modified +Version made by someone other than you, you are nevertheless required +to ensure that your Modified Version complies with the requirements of +this license. + +(12) This license does not grant you the right to use any trademark, +service mark, tradename, or logo of the Copyright Holder. + +(13) This license includes the non-exclusive, worldwide, +free-of-charge patent license to make, have made, use, offer to sell, +sell, import and otherwise transfer the Package with respect to any +patent claims licensable by the Copyright Holder that are necessarily +infringed by the Package. If you institute patent litigation +(including a cross-claim or counterclaim) against any party alleging +that the Package constitutes direct or contributory patent +infringement, then this Artistic License to you shall terminate on the +date that such litigation is filed. + +(14) Disclaimer of Warranty: +THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS +IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR +NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL +LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua b/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua new file mode 100644 index 0000000..ef3def9 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/LibAddonMenu-2.0.lua @@ -0,0 +1,809 @@ +-- LibAddonMenu-2.0 & its files © Ryan Lakanen (Seerah) -- +-- Distributed under The Artistic License 2.0 (see LICENSE) -- +------------------------------------------------------------------ + + +--Register LAM with LibStub +local MAJOR, MINOR = "LibAddonMenu-2.0", 18 +local lam, oldminor = LibStub:NewLibrary(MAJOR, MINOR) +if not lam then return end --the same or newer version of this lib is already loaded into memory + +local messages = {} +local MESSAGE_PREFIX = "[LAM2] " +local function PrintLater(msg) + if CHAT_SYSTEM.primaryContainer then + d(MESSAGE_PREFIX .. msg) + else + messages[#messages + 1] = msg + end +end + +local function FlushMessages() + for i = 1, #messages do + d(MESSAGE_PREFIX .. messages[i]) + end + messages = {} +end + +if LAMSettingsPanelCreated and not LAMCompatibilityWarning then + PrintLater("An old version of LibAddonMenu with compatibility issues was detected. For more information on how to proceed search for LibAddonMenu on esoui.com") + LAMCompatibilityWarning = true +end + +--UPVALUES-- +local wm = WINDOW_MANAGER +local em = EVENT_MANAGER +local sm = SCENE_MANAGER +local cm = CALLBACK_MANAGER +local tconcat = table.concat +local tinsert = table.insert + +local addonsForList = {} +local addonToOptionsMap = {} +local optionsCreated = {} +lam.widgets = lam.widgets or {} +local widgets = lam.widgets +lam.util = {} +local util = lam.util + +local function GetTooltipText(tooltip) + if type(tooltip) == "string" then + return tooltip + elseif type(tooltip) == "function" then + return tostring(tooltip()) + end + return nil +end + +local function CreateBaseControl(parent, controlData, controlName) + local control = wm:CreateControl(controlName or controlData.reference, parent.scroll or parent, CT_CONTROL) + control.panel = parent.panel or parent -- if this is in a submenu, panel is the submenu's parent + control.data = controlData + + control.isHalfWidth = controlData.width == "half" + control:SetWidth(control.panel:GetWidth() - 60) + return control +end + +local MIN_HEIGHT = 26 +local HALF_WIDTH_LINE_SPACING = 2 +local function CreateLabelAndContainerControl(parent, controlData, controlName) + local control = CreateBaseControl(parent, controlData, controlName) + local width = control:GetWidth() + + local container = wm:CreateControl(nil, control, CT_CONTROL) + container:SetDimensions(width / 3, MIN_HEIGHT) + control.container = container + + local label = wm:CreateControl(nil, control, CT_LABEL) + label:SetFont("ZoFontWinH4") + label:SetHeight(MIN_HEIGHT) + label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) + label:SetText(controlData.name) + control.label = label + + if control.isHalfWidth then + control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING) + label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0) + label:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0) + container:SetAnchor(TOPRIGHT, control.label, BOTTOMRIGHT, 0, HALF_WIDTH_LINE_SPACING) + else + control:SetDimensions(width, MIN_HEIGHT) + container:SetAnchor(TOPRIGHT, control, TOPRIGHT, 0, 0) + label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 0) + label:SetAnchor(TOPRIGHT, container, TOPLEFT, 5, 0) + end + + control.data.tooltipText = GetTooltipText(control.data.tooltip) + control:SetMouseEnabled(true) + control:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + control:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + return control +end + +util.GetTooltipText = GetTooltipText +util.CreateBaseControl = CreateBaseControl +util.CreateLabelAndContainerControl = CreateLabelAndContainerControl + +local ADDON_DATA_TYPE = 1 +local RESELECTING_DURING_REBUILD = true +local USER_REQUESTED_OPEN = true + + +--INTERNAL FUNCTION +--scrolls ZO_ScrollList `list` to move the row corresponding to `data` +-- into view (does nothing if there is no such row in the list) +--unlike ZO_ScrollList_ScrollDataIntoView, this function accounts for +-- fading near the list's edges - it avoids the fading area by scrolling +-- a little further than the ZO function +local function ScrollDataIntoView(list, data) + local targetIndex = data.sortIndex + if not targetIndex then return end + + local scrollMin, scrollMax = list.scrollbar:GetMinMax() + local scrollTop = list.scrollbar:GetValue() + local controlHeight = list.controlHeight + local targetMin = controlHeight * (targetIndex - 1) - 64 + -- subtracting 64 ain't arbitrary, it's the maximum fading height + -- (libraries/zo_templates/scrolltemplates.lua/UpdateScrollFade) + + if targetMin < scrollTop then + ZO_ScrollList_ScrollAbsolute(list, zo_max(targetMin, scrollMin)) + else + local listHeight = ZO_ScrollList_GetHeight(list) + local targetMax = controlHeight * targetIndex + 64 - listHeight + + if targetMax > scrollTop then + ZO_ScrollList_ScrollAbsolute(list, zo_min(targetMax, scrollMax)) + end + end +end + + +--INTERNAL FUNCTION +--constructs a string pattern from the text in `searchEdit` control +-- * metacharacters are escaped, losing their special meaning +-- * whitespace matches anything (including empty substring) +--if there is nothing but whitespace, returns nil +--otherwise returns a filter function, which takes a `data` table argument +-- and returns true iff `data.filterText` matches the pattern +local function GetSearchFilterFunc(searchEdit) + local text = searchEdit:GetText():lower() + local pattern = text:match("(%S+.-)%s*$") + + if not pattern then -- nothing but whitespace + return nil + end + + -- escape metacharacters, e.g. "ESO-Datenbank.de" => "ESO%-Datenbank%.de" + pattern = pattern:gsub("[-*+?^$().[%]%%]", "%%%0") + + -- replace whitespace with "match shortest anything" + pattern = pattern:gsub("%s+", ".-") + + return function(data) + return data.filterText:lower():find(pattern) ~= nil + end +end + + +--INTERNAL FUNCTION +--populates `addonList` with entries from `addonsForList` +-- addonList = ZO_ScrollList control +-- filter = [optional] function(data) +local function PopulateAddonList(addonList, filter) + local entryList = ZO_ScrollList_GetDataList(addonList) + local numEntries = 0 + local selectedData = nil + + ZO_ScrollList_Clear(addonList) + + for i, data in ipairs(addonsForList) do + if not filter or filter(data) then + local dataEntry = ZO_ScrollList_CreateDataEntry(ADDON_DATA_TYPE, data) + numEntries = numEntries + 1 + data.sortIndex = numEntries + entryList[numEntries] = dataEntry + -- select the first panel passing the filter, or the currently + -- shown panel, but only if it passes the filter as well + if selectedData == nil or data.panel == lam.currentAddonPanel then + selectedData = data + end + else + data.sortIndex = nil + end + end + + ZO_ScrollList_Commit(addonList) + + if selectedData then + if selectedData.panel == lam.currentAddonPanel then + ZO_ScrollList_SelectData(addonList, selectedData, nil, RESELECTING_DURING_REBUILD) + else + ZO_ScrollList_SelectData(addonList, selectedData, nil) + end + ScrollDataIntoView(addonList, selectedData) + end +end + + +--METHOD: REGISTER WIDGET-- +--each widget has its version checked before loading, +--so we only have the most recent one in memory +--Usage: +-- widgetType = "string"; the type of widget being registered +-- widgetVersion = integer; the widget's version number +LAMCreateControl = LAMCreateControl or {} +local lamcc = LAMCreateControl + +function lam:RegisterWidget(widgetType, widgetVersion) + if widgets[widgetType] and widgets[widgetType] >= widgetVersion then + return false + else + widgets[widgetType] = widgetVersion + return true + end +end + + +--METHOD: OPEN TO ADDON PANEL-- +--opens to a specific addon's option panel +--Usage: +-- panel = userdata; the panel returned by the :RegisterOptionsPanel method +local locSettings = GetString(SI_GAME_MENU_SETTINGS) +function lam:OpenToPanel(panel) + + -- find and select the panel's row in addon list + + local addonList = lam.addonList + local selectedData = nil + + for _, addonData in ipairs(addonsForList) do + if addonData.panel == panel then + selectedData = addonData + ScrollDataIntoView(addonList, selectedData) + break + end + end + + ZO_ScrollList_SelectData(addonList, selectedData) + ZO_ScrollList_RefreshVisible(addonList, selectedData) + + local srchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit") + srchEdit:Clear() + + -- note that ZO_ScrollList doesn't require `selectedData` to be actually + -- present in the list, and that the list will only be populated once LAM + -- "Addon Settings" menu entry is selected for the first time + + local function openAddonSettingsMenu() + local gameMenu = ZO_GameMenu_InGame.gameMenu + local settingsMenu = gameMenu.headerControls[locSettings] + + if settingsMenu then -- an instance of ZO_TreeNode + local children = settingsMenu:GetChildren() + for i = 1, (children and #children or 0) do + local childNode = children[i] + local data = childNode:GetData() + if data and data.id == lam.panelId then + -- found LAM "Addon Settings" node, yay! + childNode:GetTree():SelectNode(childNode) + break + end + end + end + end + + if sm:GetScene("gameMenuInGame"):GetState() == SCENE_SHOWN then + openAddonSettingsMenu() + else + sm:CallWhen("gameMenuInGame", SCENE_SHOWN, openAddonSettingsMenu) + sm:Show("gameMenuInGame") + end +end + + +--INTERNAL FUNCTION +--creates controls when options panel is first shown +--controls anchoring of these controls in the panel +local function CreateOptionsControls(panel) + local addonID = panel:GetName() + local optionsTable = addonToOptionsMap[addonID] + + if optionsTable then + local function CreateAndAnchorWidget(parent, widgetData, offsetX, offsetY, anchorTarget, wasHalf) + local widget + local status, err = pcall(function() widget = LAMCreateControl[widgetData.type](parent, widgetData) end) + if not status then + return err or true, offsetY, anchorTarget, wasHalf + else + local isHalf = (widgetData.width == "half") + if not anchorTarget then -- the first widget in a panel is just placed in the top left corner + widget:SetAnchor(TOPLEFT) + anchorTarget = widget + elseif wasHalf and isHalf then -- when the previous widget was only half width and this one is too, we place it on the right side + widget:SetAnchor(TOPLEFT, anchorTarget, TOPRIGHT, 5 + (offsetX or 0), 0) + widget.lineControl = anchorTarget + offsetY = zo_max(0, widget:GetHeight() - anchorTarget:GetHeight()) -- we need to get the common height of both widgets to know where the next row starts + isHalf = false + else -- otherwise we just put it below the previous one normally + widget:SetAnchor(TOPLEFT, anchorTarget, BOTTOMLEFT, 0, 15 + offsetY) + offsetY = 0 + anchorTarget = widget + end + return false, offsetY, anchorTarget, isHalf + end + end + + local THROTTLE_TIMEOUT, THROTTLE_COUNT = 10, 20 + local fifo = {} + local anchorOffset, lastAddedControl, wasHalf + local CreateWidgetsInPanel, err + + local function PrepareForNextPanel() + anchorOffset, lastAddedControl, wasHalf = 0, nil, false + end + + local function SetupCreationCalls(parent, widgetDataTable) + fifo[#fifo + 1] = PrepareForNextPanel + local count = #widgetDataTable + for i = 1, count, THROTTLE_COUNT do + fifo[#fifo + 1] = function() + CreateWidgetsInPanel(parent, widgetDataTable, i, zo_min(i + THROTTLE_COUNT - 1, count)) + end + end + return count ~= NonContiguousCount(widgetDataTable) + end + + CreateWidgetsInPanel = function(parent, widgetDataTable, startIndex, endIndex) + for i=startIndex,endIndex do + local widgetData = widgetDataTable[i] + if not widgetData then + PrintLater("Skipped creation of missing entry in the settings menu of " .. addonID .. ".") + else + local widgetType = widgetData.type + local offsetX = 0 + local isSubmenu = (widgetType == "submenu") + if isSubmenu then + wasHalf = false + offsetX = 5 + end + + err, anchorOffset, lastAddedControl, wasHalf = CreateAndAnchorWidget(parent, widgetData, offsetX, anchorOffset, lastAddedControl, wasHalf) + if err then + PrintLater(("Could not create %s '%s' of %s."):format(widgetData.type, widgetData.name or "unnamed", addonID)) + end + + if isSubmenu then + if SetupCreationCalls(lastAddedControl, widgetData.controls) then + PrintLater(("The sub menu '%s' of %s is missing some entries."):format(widgetData.name or "unnamed", addonID)) + end + end + end + end + end + + local function DoCreateSettings() + if #fifo > 0 then + local nextCall = table.remove(fifo, 1) + nextCall() + if(nextCall == PrepareForNextPanel) then + DoCreateSettings() + else + zo_callLater(DoCreateSettings, THROTTLE_TIMEOUT) + end + else + optionsCreated[addonID] = true + cm:FireCallbacks("LAM-PanelControlsCreated", panel) + end + end + + if SetupCreationCalls(panel, optionsTable) then + PrintLater(("The settings menu of %s is missing some entries."):format(addonID)) + end + DoCreateSettings() + end +end + + +--INTERNAL FUNCTION +--handles switching between panels +local function ToggleAddonPanels(panel) --called in OnShow of newly shown panel + local currentlySelected = lam.currentAddonPanel + if currentlySelected and currentlySelected ~= panel then + currentlySelected:SetHidden(true) + end + lam.currentAddonPanel = panel + + -- refresh visible rows to reflect panel IsHidden status + ZO_ScrollList_RefreshVisible(lam.addonList) + + if not optionsCreated[panel:GetName()] then --if this is the first time opening this panel, create these options + CreateOptionsControls(panel) + end + + cm:FireCallbacks("LAM-RefreshPanel", panel) +end + +local CheckSafetyAndInitialize + +--METHOD: REGISTER ADDON PANEL +--registers your addon with LibAddonMenu and creates a panel +--Usage: +-- addonID = "string"; unique ID which will be the global name of your panel +-- panelData = table; data object for your panel - see controls\panel.lua +function lam:RegisterAddonPanel(addonID, panelData) + CheckSafetyAndInitialize(addonID) + local container = lam:GetAddonPanelContainer() + local panel = lamcc.panel(container, panelData, addonID) --addonID==global name of panel + panel:SetHidden(true) + panel:SetAnchorFill(container) + panel:SetHandler("OnShow", ToggleAddonPanels) + + local function stripMarkup(str) + return str:gsub("|[Cc]%x%x%x%x%x%x", ""):gsub("|[Rr]", "") + end + + local filterParts = {panelData.name, nil, nil} + -- append keywords and author separately, the may be nil + filterParts[#filterParts + 1] = panelData.keywords + filterParts[#filterParts + 1] = panelData.author + + local addonData = { + panel = panel, + name = stripMarkup(panelData.name), + filterText = stripMarkup(tconcat(filterParts, "\t")):lower(), + } + + tinsert(addonsForList, addonData) + + if panelData.slashCommand then + SLASH_COMMANDS[panelData.slashCommand] = function() + lam:OpenToPanel(panel) + end + end + + return panel --return for authors creating options manually +end + + +--METHOD: REGISTER OPTION CONTROLS +--registers the options you want shown for your addon +--these are stored in a table where each key-value pair is the order +--of the options in the panel and the data for that control, respectively +--see exampleoptions.lua for an example +--see controls\.lua for each widget type +--Usage: +-- addonID = "string"; the same string passed to :RegisterAddonPanel +-- optionsTable = table; the table containing all of the options controls and their data +function lam:RegisterOptionControls(addonID, optionsTable) --optionsTable = {sliderData, buttonData, etc} + addonToOptionsMap[addonID] = optionsTable +end + + +--INTERNAL FUNCTION +--creates LAM's Addon Settings entry in ZO_GameMenu +local function CreateAddonSettingsMenuEntry() + --Russian for TERAB1T's RuESO addon, which creates an "ru" locale + --game font does not support Cyrillic, so they are using custom fonts + extended latin charset + --Spanish provided by Luisen75 for their translation project + local controlPanelNames = { + en = "Addon Settings", + fr = "Extensions", + de = "Erweiterungen", + ru = "Îacòpoéêè äoïoìîeîèé", + es = "Configura Addons", + } + + local panelData = { + id = KEYBOARD_OPTIONS.currentPanelId, + name = controlPanelNames[GetCVar("Language.2")] or controlPanelNames["en"], + } + + KEYBOARD_OPTIONS.currentPanelId = panelData.id + 1 + KEYBOARD_OPTIONS.panelNames[panelData.id] = panelData.name + + lam.panelId = panelData.id + + local addonListSorted = false + + function panelData.callback() + sm:AddFragment(lam:GetAddonSettingsFragment()) + KEYBOARD_OPTIONS:ChangePanels(lam.panelId) + + local title = LAMAddonSettingsWindow:GetNamedChild("Title") + title:SetText(panelData.name) + + if not addonListSorted and #addonsForList > 0 then + local searchEdit = LAMAddonSettingsWindow:GetNamedChild("SearchFilterEdit") + --we're about to show our list for the first time - let's sort it + table.sort(addonsForList, function(a, b) return a.name < b.name end) + PopulateAddonList(lam.addonList, GetSearchFilterFunc(searchEdit)) + addonListSorted = true + end + end + + function panelData.unselectedCallback() + sm:RemoveFragment(lam:GetAddonSettingsFragment()) + if SetCameraOptionsPreviewModeEnabled then -- available since API version 100011 + SetCameraOptionsPreviewModeEnabled(false) + end + end + + ZO_GameMenu_AddSettingPanel(panelData) +end + + +--INTERNAL FUNCTION +--creates the left-hand menu in LAM's window +local function CreateAddonList(name, parent) + local addonList = wm:CreateControlFromVirtual(name, parent, "ZO_ScrollList") + + local function addonListRow_OnMouseDown(control, button) + if button == 1 then + local data = ZO_ScrollList_GetData(control) + ZO_ScrollList_SelectData(addonList, data, control) + end + end + + local function addonListRow_OnMouseEnter(control) + ZO_ScrollList_MouseEnter(addonList, control) + end + + local function addonListRow_OnMouseExit(control) + ZO_ScrollList_MouseExit(addonList, control) + end + + local function addonListRow_Select(previouslySelectedData, selectedData, reselectingDuringRebuild) + if not reselectingDuringRebuild then + if previouslySelectedData then + previouslySelectedData.panel:SetHidden(true) + end + if selectedData then + selectedData.panel:SetHidden(false) + PlaySound(SOUNDS.MENU_SUBCATEGORY_SELECTION) + end + end + end + + local function addonListRow_Setup(control, data) + control:SetText(data.name) + control:SetSelected(not data.panel:IsHidden()) + end + + ZO_ScrollList_AddDataType(addonList, ADDON_DATA_TYPE, "ZO_SelectableLabel", 28, addonListRow_Setup) + -- I don't know how to make highlights clear properly; they often + -- get stuck and after a while the list is full of highlighted rows + --ZO_ScrollList_EnableHighlight(addonList, "ZO_ThinListHighlight") + ZO_ScrollList_EnableSelection(addonList, "ZO_ThinListHighlight", addonListRow_Select) + + local addonDataType = ZO_ScrollList_GetDataTypeTable(addonList, ADDON_DATA_TYPE) + local addonListRow_CreateRaw = addonDataType.pool.m_Factory + + local function addonListRow_Create(pool) + local control = addonListRow_CreateRaw(pool) + control:SetHandler("OnMouseDown", addonListRow_OnMouseDown) + --control:SetHandler("OnMouseEnter", addonListRow_OnMouseEnter) + --control:SetHandler("OnMouseExit", addonListRow_OnMouseExit) + control:SetHeight(28) + control:SetFont("ZoFontHeader") + control:SetHorizontalAlignment(TEXT_ALIGN_LEFT) + control:SetVerticalAlignment(TEXT_ALIGN_CENTER) + control:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) + return control + end + + addonDataType.pool.m_Factory = addonListRow_Create + + return addonList +end + + +--INTERNAL FUNCTION +local function CreateSearchFilterBox(name, parent) + local boxControl = wm:CreateControl(name, parent, CT_CONTROL) + + local srchButton = wm:CreateControl("$(parent)Button", boxControl, CT_BUTTON) + srchButton:SetDimensions(32, 32) + srchButton:SetAnchor(LEFT, nil, LEFT, 2, 0) + srchButton:SetNormalTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_up.dds") + srchButton:SetPressedTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_down.dds") + srchButton:SetMouseOverTexture("EsoUI/Art/LFG/LFG_tabIcon_groupTools_over.dds") + + local srchEdit = wm:CreateControlFromVirtual("$(parent)Edit", boxControl, "ZO_DefaultEdit") + srchEdit:SetAnchor(LEFT, srchButton, RIGHT, 4, 1) + srchEdit:SetAnchor(RIGHT, nil, RIGHT, -4, 1) + srchEdit:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) + + local srchBg = wm:CreateControl("$(parent)Bg", boxControl, CT_BACKDROP) + srchBg:SetAnchorFill() + srchBg:SetAlpha(0) + srchBg:SetCenterColor(0, 0, 0, 0.5) + srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA()) + srchBg:SetEdgeTexture("", 1, 1, 0, 0) + + -- search backdrop should appear whenever you hover over either + -- the magnifying glass button or the edit field (which is only + -- visible when it contains some text), and also while the edit + -- field has keyboard focus + + local srchActive = false + local srchHover = false + + local function srchBgUpdateAlpha() + if srchActive or srchEdit:HasFocus() then + srchBg:SetAlpha(srchHover and 0.8 or 0.6) + else + srchBg:SetAlpha(srchHover and 0.6 or 0.0) + end + end + + local function srchMouseEnter(control) + srchHover = true + srchBgUpdateAlpha() + end + + local function srchMouseExit(control) + srchHover = false + srchBgUpdateAlpha() + end + + boxControl:SetMouseEnabled(true) + boxControl:SetHitInsets(1, 1, -1, -1) + boxControl:SetHandler("OnMouseEnter", srchMouseEnter) + boxControl:SetHandler("OnMouseExit", srchMouseExit) + + srchButton:SetHandler("OnMouseEnter", srchMouseEnter) + srchButton:SetHandler("OnMouseExit", srchMouseExit) + + local focusLostTime = 0 + + srchButton:SetHandler("OnClicked", function(self) + srchEdit:Clear() + if GetFrameTimeMilliseconds() - focusLostTime < 100 then + -- re-focus the edit box if it lost focus due to this + -- button click (note that this handler may run a few + -- frames later) + srchEdit:TakeFocus() + end + end) + + srchEdit:SetHandler("OnMouseEnter", srchMouseEnter) + srchEdit:SetHandler("OnMouseExit", srchMouseExit) + srchEdit:SetHandler("OnFocusGained", srchBgUpdateAlpha) + + srchEdit:SetHandler("OnFocusLost", function() + focusLostTime = GetFrameTimeMilliseconds() + srchBgUpdateAlpha() + end) + + srchEdit:SetHandler("OnEscape", function(self) + self:Clear() + self:LoseFocus() + end) + + srchEdit:SetHandler("OnTextChanged", function(self) + local filterFunc = GetSearchFilterFunc(self) + if filterFunc then + srchActive = true + srchBg:SetEdgeColor(ZO_SECOND_CONTRAST_TEXT:UnpackRGBA()) + srchButton:SetState(BSTATE_PRESSED) + else + srchActive = false + srchBg:SetEdgeColor(ZO_DISABLED_TEXT:UnpackRGBA()) + srchButton:SetState(BSTATE_NORMAL) + end + srchBgUpdateAlpha() + PopulateAddonList(lam.addonList, filterFunc) + PlaySound(SOUNDS.SPINNER_DOWN) + end) + + return boxControl +end + + +--INTERNAL FUNCTION +--creates LAM's Addon Settings top-level window +local function CreateAddonSettingsWindow() + local tlw = wm:CreateTopLevelWindow("LAMAddonSettingsWindow") + tlw:SetHidden(true) + tlw:SetDimensions(1010, 914) -- same height as ZO_OptionsWindow + + ZO_ReanchorControlForLeftSidePanel(tlw) + + -- create black background for the window (mimic ZO_RightFootPrintBackground) + + local bgLeft = wm:CreateControl("$(parent)BackgroundLeft", tlw, CT_TEXTURE) + bgLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_left.dds") + bgLeft:SetDimensions(1024, 1024) + bgLeft:SetAnchor(TOPLEFT, nil, TOPLEFT) + bgLeft:SetDrawLayer(DL_BACKGROUND) + bgLeft:SetExcludeFromResizeToFitExtents(true) + + local bgRight = wm:CreateControl("$(parent)BackgroundRight", tlw, CT_TEXTURE) + bgRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_right.dds") + bgRight:SetDimensions(64, 1024) + bgRight:SetAnchor(TOPLEFT, bgLeft, TOPRIGHT) + bgRight:SetDrawLayer(DL_BACKGROUND) + bgRight:SetExcludeFromResizeToFitExtents(true) + + -- create gray background for addon list (mimic ZO_TreeUnderlay) + + local underlayLeft = wm:CreateControl("$(parent)UnderlayLeft", tlw, CT_TEXTURE) + underlayLeft:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_left.dds") + underlayLeft:SetDimensions(256, 1024) + underlayLeft:SetAnchor(TOPLEFT, bgLeft, TOPLEFT) + underlayLeft:SetDrawLayer(DL_BACKGROUND) + underlayLeft:SetExcludeFromResizeToFitExtents(true) + + local underlayRight = wm:CreateControl("$(parent)UnderlayRight", tlw, CT_TEXTURE) + underlayRight:SetTexture("EsoUI/Art/Miscellaneous/centerscreen_indexArea_right.dds") + underlayRight:SetDimensions(128, 1024) + underlayRight:SetAnchor(TOPLEFT, underlayLeft, TOPRIGHT) + underlayRight:SetDrawLayer(DL_BACKGROUND) + underlayRight:SetExcludeFromResizeToFitExtents(true) + + -- create title bar (mimic ZO_OptionsWindow) + + local title = wm:CreateControl("$(parent)Title", tlw, CT_LABEL) + title:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 70) + title:SetFont("ZoFontWinH1") + title:SetModifyTextType(MODIFY_TEXT_TYPE_UPPERCASE) + + local divider = wm:CreateControlFromVirtual("$(parent)Divider", tlw, "ZO_Options_Divider") + divider:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 108) + + -- create search filter box + + local srchBox = CreateSearchFilterBox("$(parent)SearchFilter", tlw) + srchBox:SetAnchor(TOPLEFT, nil, TOPLEFT, 63, 120) + srchBox:SetDimensions(260, 30) + + -- create scrollable addon list + + local addonList = CreateAddonList("$(parent)AddonList", tlw) + addonList:SetAnchor(TOPLEFT, nil, TOPLEFT, 65, 160) + addonList:SetDimensions(285, 665) + + lam.addonList = addonList -- for easy access from elsewhere + + -- create container for option panels + + local panelContainer = wm:CreateControl("$(parent)PanelContainer", tlw, CT_CONTROL) + panelContainer:SetAnchor(TOPLEFT, nil, TOPLEFT, 365, 120) + panelContainer:SetDimensions(645, 675) + + return tlw +end + + +--INITIALIZING +local safeToInitialize = false +local hasInitialized = false + +local eventHandle = table.concat({MAJOR, MINOR}, "r") +local function OnLoad(_, addonName) + -- wait for the first loaded event + em:UnregisterForEvent(eventHandle, EVENT_ADD_ON_LOADED) + safeToInitialize = true +end +em:RegisterForEvent(eventHandle, EVENT_ADD_ON_LOADED, OnLoad) + +local function OnActivated(_, addonName) + em:UnregisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED) + FlushMessages() +end +em:RegisterForEvent(eventHandle, EVENT_PLAYER_ACTIVATED, OnActivated) + +function CheckSafetyAndInitialize(addonID) + if not safeToInitialize then + local msg = string.format("The panel with id '%s' was registered before addon loading has completed. This might break the AddOn Settings menu.", addonID) + PrintLater(msg) + end + if not hasInitialized then + hasInitialized = true + end +end + + +--TODO documentation +function lam:GetAddonPanelContainer() + local fragment = lam:GetAddonSettingsFragment() + local window = fragment:GetControl() + return window:GetNamedChild("PanelContainer") +end + + +--TODO documentation +function lam:GetAddonSettingsFragment() + assert(hasInitialized or safeToInitialize) + if not LAMAddonSettingsFragment then + local window = CreateAddonSettingsWindow() + LAMAddonSettingsFragment = ZO_FadeSceneFragment:New(window, true, 100) + CreateAddonSettingsMenuEntry() + end + return LAMAddonSettingsFragment +end + + +-- vi: noexpandtab diff --git a/Libs/LibAddonMenu-2.0/controls/button.lua b/Libs/LibAddonMenu-2.0/controls/button.lua new file mode 100644 index 0000000..7dad09e --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/button.lua @@ -0,0 +1,89 @@ +--[[buttonData = { + type = "button", + name = "My Button", + tooltip = "Button's tooltip text.", + func = function() end, + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + icon = "icon\\path.dds", --(optional) + warning = "Will need to reload the UI.", --(optional) + reference = "MyAddonButton" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 7 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("button", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local tinsert = table.insert + +local function UpdateDisabled(control) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.button:SetEnabled(not disable) +end + + +--controlName is optional +local MIN_HEIGHT = 28 -- default_button height +local HALF_WIDTH_LINE_SPACING = 2 +function LAMCreateControl.button(parent, buttonData, controlName) + local control = LAM.util.CreateBaseControl(parent, buttonData, controlName) + control:SetMouseEnabled(true) + + local width = control:GetWidth() + if control.isHalfWidth then + control:SetDimensions(width / 2, MIN_HEIGHT * 2 + HALF_WIDTH_LINE_SPACING) + else + control:SetDimensions(width, MIN_HEIGHT) + end + + if buttonData.icon then + control.button = wm:CreateControl(nil, control, CT_BUTTON) + control.button:SetDimensions(26, 26) + control.button:SetNormalTexture(buttonData.icon) + control.button:SetPressedOffset(2, 2) + else + --control.button = wm:CreateControlFromVirtual(controlName.."Button", control, "ZO_DefaultButton") + control.button = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultButton") + control.button:SetWidth(width / 3) + control.button:SetText(buttonData.name) + end + local button = control.button + button:SetAnchor(control.isHalfWidth and CENTER or RIGHT) + button:SetClickSound("Click") + button.data = {tooltipText = LAM.util.GetTooltipText(buttonData.tooltip)} + button:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + button:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + button:SetHandler("OnClicked", function(self, ...) + buttonData.func(self, ...) + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + end) + + if buttonData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, button, LEFT, -5, 0) + control.warning.data = {tooltipText = buttonData.warning} + end + + if buttonData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + + --this is here because buttons don't have an UpdateValue method + if control.panel.data.registerForRefresh then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/checkbox.lua b/Libs/LibAddonMenu-2.0/controls/checkbox.lua new file mode 100644 index 0000000..8401dda --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/checkbox.lua @@ -0,0 +1,144 @@ +--[[checkboxData = { + type = "checkbox", + name = "My Checkbox", + tooltip = "Checkbox's tooltip text.", + getFunc = function() return db.var end, + setFunc = function(value) db.var = value doStuff() end, + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "Will need to reload the UI.", --(optional) + default = defaults.var, --(optional) + reference = "MyAddonCheckbox" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 9 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("checkbox", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local tinsert = table.insert +--label +local enabledColor = ZO_DEFAULT_ENABLED_COLOR +local enabledHLcolor = ZO_HIGHLIGHT_TEXT +local disabledColor = ZO_DEFAULT_DISABLED_COLOR +local disabledHLcolor = ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR +--checkbox +local checkboxColor = ZO_NORMAL_TEXT +local checkboxHLcolor = ZO_HIGHLIGHT_TEXT + + +local function UpdateDisabled(control) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.label:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or control.value and ZO_DEFAULT_ENABLED_COLOR or ZO_DEFAULT_DISABLED_COLOR):UnpackRGBA()) + control.checkbox:SetColor((disable and ZO_DEFAULT_DISABLED_COLOR or ZO_NORMAL_TEXT):UnpackRGBA()) + --control:SetMouseEnabled(not disable) + --control:SetMouseEnabled(true) + + control.isDisabled = disable +end + +local function ToggleCheckbox(control) + if control.value then + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.checkbox:SetText(control.checkedText) + else + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.checkbox:SetText(control.uncheckedText) + end +end + +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = control.data.default + control.data.setFunc(value) + elseif value ~= nil then --our value could be false + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + else + value = control.data.getFunc() + end + control.value = value + + ToggleCheckbox(control) +end + +local function OnMouseEnter(control) + ZO_Options_OnMouseEnter(control) + + if control.isDisabled then return end + + local label = control.label + if control.value then + label:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) + else + label:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) + end + control.checkbox:SetColor(ZO_HIGHLIGHT_TEXT:UnpackRGBA()) +end + +local function OnMouseExit(control) + ZO_Options_OnMouseExit(control) + + if control.isDisabled then return end + + local label = control.label + if control.value then + label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + else + label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + end + control.checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) +end + +--controlName is optional +function LAMCreateControl.checkbox(parent, checkboxData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, checkboxData, controlName) + control:SetHandler("OnMouseEnter", OnMouseEnter) + control:SetHandler("OnMouseExit", OnMouseExit) + control:SetHandler("OnMouseUp", function(control) + if control.isDisabled then return end + PlaySound(SOUNDS.DEFAULT_CLICK) + control.value = not control.value + control:UpdateValue(false, control.value) + end) + + control.checkbox = wm:CreateControl(nil, control.container, CT_LABEL) + local checkbox = control.checkbox + checkbox:SetAnchor(LEFT, control.container, LEFT, 0, 0) + checkbox:SetFont("ZoFontGameBold") + checkbox:SetColor(ZO_NORMAL_TEXT:UnpackRGBA()) + control.checkedText = GetString(SI_CHECK_BUTTON_ON):upper() + control.uncheckedText = GetString(SI_CHECK_BUTTON_OFF):upper() + + if checkboxData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, checkbox, LEFT, -5, 0) + control.warning.data = {tooltipText = checkboxData.warning} + end + + control.data.tooltipText = LAM.util.GetTooltipText(checkboxData.tooltip) + + if checkboxData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + control.UpdateValue = UpdateValue + control:UpdateValue() + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/colorpicker.lua b/Libs/LibAddonMenu-2.0/controls/colorpicker.lua new file mode 100644 index 0000000..011b4b5 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/colorpicker.lua @@ -0,0 +1,110 @@ +--[[colorpickerData = { + type = "colorpicker", + name = "My Color Picker", + tooltip = "Color Picker's tooltip text.", + getFunc = function() return db.r, db.g, db.b, db.a end, --(alpha is optional) + setFunc = function(r,g,b,a) db.r=r, db.g=g, db.b=b, db.a=a end, --(alpha is optional) + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "Will need to reload the UI.", --(optional) + default = {r = defaults.r, g = defaults.g, b = defaults.b, a = defaults.a}, --(optional) table of default color values (or default = defaultColor, where defaultColor is a table with keys of r, g, b[, a]) + reference = "MyAddonColorpicker" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 7 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("colorpicker", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local tinsert = table.insert + + +local function UpdateDisabled(control) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end + + control.isDisabled = disable +end + +local function UpdateValue(control, forceDefault, valueR, valueG, valueB, valueA) + if forceDefault then --if we are forcing defaults + local color = control.data.default + valueR, valueG, valueB, valueA = color.r, color.g, color.b, color.a + control.data.setFunc(valueR, valueG, valueB, valueA) + elseif valueR and valueG and valueB then + control.data.setFunc(valueR, valueG, valueB, valueA or 1) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + else + valueR, valueG, valueB, valueA = control.data.getFunc() + end + + control.thumb:SetColor(valueR, valueG, valueB, valueA or 1) +end + +function LAMCreateControl.colorpicker(parent, colorpickerData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, colorpickerData, controlName) + + control.color = control.container + local color = control.color + + control.thumb = wm:CreateControl(nil, color, CT_TEXTURE) + local thumb = control.thumb + thumb:SetDimensions(36, 18) + thumb:SetAnchor(LEFT, color, LEFT, 4, 0) + + color.border = wm:CreateControl(nil, color, CT_TEXTURE) + local border = color.border + border:SetTexture("EsoUI\\Art\\ChatWindow\\chatOptions_bgColSwatch_frame.dds") + border:SetTextureCoords(0, .625, 0, .8125) + border:SetDimensions(40, 22) + border:SetAnchor(CENTER, thumb, CENTER, 0, 0) + + local function ColorPickerCallback(r, g, b, a) + control:UpdateValue(false, r, g, b, a) + end + + control:SetHandler("OnMouseUp", function(self, btn, upInside) + if self.isDisabled then return end + + if upInside then + local r, g, b, a = colorpickerData.getFunc() + COLOR_PICKER:Show(ColorPickerCallback, r, g, b, a, colorpickerData.name) + end + end) + + if colorpickerData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, control.color, LEFT, -5, 0) + control.warning.data = {tooltipText = colorpickerData.warning} + end + + control.data.tooltipText = LAM.util.GetTooltipText(colorpickerData.tooltip) + + if colorpickerData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + control.UpdateValue = UpdateValue + control:UpdateValue() + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/custom.lua b/Libs/LibAddonMenu-2.0/controls/custom.lua new file mode 100644 index 0000000..e7b7312 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/custom.lua @@ -0,0 +1,40 @@ +--[[customData = { + type = "custom", + reference = "MyAddonCustomControl", --(optional) unique name for your control to use as reference + refreshFunc = function(customControl) end, --(optional) function to call when panel/controls refresh + width = "full", --or "half" (optional) +} ]] + +local widgetVersion = 6 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("custom", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local tinsert = table.insert + +local function UpdateValue(control) + if control.data.refreshFunc then + control.data.refreshFunc(control) + end +end + +local MIN_HEIGHT = 26 +function LAMCreateControl.custom(parent, customData, controlName) + local control = LAM.util.CreateBaseControl(parent, customData, controlName) + local width = control:GetWidth() + control:SetResizeToFitDescendents(true) + + if control.isHalfWidth then --note these restrictions + control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4) + else + control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4) + end + + control.UpdateValue = UpdateValue + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/description.lua b/Libs/LibAddonMenu-2.0/controls/description.lua new file mode 100644 index 0000000..1929d5a --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/description.lua @@ -0,0 +1,64 @@ +--[[descriptionData = { + type = "description", + title = "My Title", --(optional) + text = "My description text to display.", + width = "full", --or "half" (optional) + reference = "MyAddonDescription" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 6 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("description", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local tinsert = table.insert + +local function UpdateValue(control) + if control.title then + control.title:SetText(control.data.title) + end + control.desc:SetText(control.data.text) +end + +local MIN_HEIGHT = 26 +function LAMCreateControl.description(parent, descriptionData, controlName) + local control = LAM.util.CreateBaseControl(parent, descriptionData, controlName) + local isHalfWidth = control.isHalfWidth + local width = control:GetWidth() + control:SetResizeToFitDescendents(true) + + if isHalfWidth then + control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4) + else + control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4) + end + + control.desc = wm:CreateControl(nil, control, CT_LABEL) + local desc = control.desc + desc:SetVerticalAlignment(TEXT_ALIGN_TOP) + desc:SetFont("ZoFontGame") + desc:SetText(descriptionData.text) + desc:SetWidth(isHalfWidth and width / 2 or width) + + if descriptionData.title then + control.title = wm:CreateControl(nil, control, CT_LABEL) + local title = control.title + title:SetWidth(isHalfWidth and width / 2 or width) + title:SetAnchor(TOPLEFT, control, TOPLEFT) + title:SetFont("ZoFontWinH4") + title:SetText(descriptionData.title) + desc:SetAnchor(TOPLEFT, title, BOTTOMLEFT) + else + desc:SetAnchor(TOPLEFT) + end + + control.UpdateValue = UpdateValue + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control + +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/dropdown.lua b/Libs/LibAddonMenu-2.0/controls/dropdown.lua new file mode 100644 index 0000000..0731e94 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/dropdown.lua @@ -0,0 +1,131 @@ +--[[dropdownData = { + type = "dropdown", + name = "My Dropdown", + tooltip = "Dropdown's tooltip text.", + choices = {"table", "of", "choices"}, + sort = "name-up", --or "name-down", "numeric-up", "numeric-down" (optional) - if not provided, list will not be sorted + getFunc = function() return db.var end, + setFunc = function(var) db.var = var doStuff() end, + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "Will need to reload the UI.", --(optional) + default = defaults.var, --(optional) + reference = "MyAddonDropdown" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 9 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("dropdown", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local tinsert = table.insert + + +local function UpdateDisabled(control) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.dropdown:SetEnabled(not disable) + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end +end + +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = control.data.default + control.data.setFunc(value) + control.dropdown:SetSelectedItem(value) + elseif value then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + else + value = control.data.getFunc() + control.dropdown:SetSelectedItem(value) + end +end + +local function DropdownCallback(control, choiceText, choice) + choice.control:UpdateValue(false, choiceText) +end + +local function UpdateChoices(control, choices) + control.dropdown:ClearItems() --remove previous choices --(need to call :SetSelectedItem()?) + + --build new list of choices + local choices = choices or control.data.choices + for i = 1, #choices do + local entry = control.dropdown:CreateItemEntry(choices[i], DropdownCallback) + entry.control = control + control.dropdown:AddItem(entry, not control.data.sort and ZO_COMBOBOX_SUPRESS_UPDATE) --if sort type/order isn't specified, then don't sort + end +end + +local function GrabSortingInfo(sortInfo) + local t, i = {}, 1 + for info in string.gmatch(sortInfo, "([^%-]+)") do + t[i] = info + i = i + 1 + end + + return t +end + +function LAMCreateControl.dropdown(parent, dropdownData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, dropdownData, controlName) + + local countControl = parent + local name = parent:GetName() + if not name or #name == 0 then + countControl = LAMCreateControl + name = "LAM" + end + local comboboxCount = (countControl.comboboxCount or 0) + 1 + countControl.comboboxCount = comboboxCount + control.combobox = wm:CreateControlFromVirtual(zo_strjoin(nil, name, "Combobox", comboboxCount), control.container, "ZO_ComboBox") + + local combobox = control.combobox + combobox:SetAnchor(TOPLEFT) + combobox:SetDimensions(control.container:GetDimensions()) + combobox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + combobox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + control.dropdown = ZO_ComboBox_ObjectFromContainer(combobox) + local dropdown = control.dropdown + if dropdownData.sort then + local sortInfo = GrabSortingInfo(dropdownData.sort) + local sortType, sortOrder = sortInfo[1], sortInfo[2] + dropdown:SetSortOrder(sortOrder == "up" and ZO_SORT_ORDER_UP or ZO_SORT_ORDER_DOWN, sortType == "name" and ZO_SORT_BY_NAME or ZO_SORT_BY_NAME_NUMERIC) + end + + if dropdownData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, combobox, LEFT, -5, 0) + control.warning.data = {tooltipText = dropdownData.warning} + end + + if dropdownData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + control.UpdateChoices = UpdateChoices + control:UpdateChoices(dropdownData.choices) + control.UpdateValue = UpdateValue + control:UpdateValue() + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/editbox.lua b/Libs/LibAddonMenu-2.0/controls/editbox.lua new file mode 100644 index 0000000..78e7683 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/editbox.lua @@ -0,0 +1,136 @@ +--[[editboxData = { + type = "editbox", + name = "My Editbox", + tooltip = "Editbox's tooltip text.", + getFunc = function() return db.text end, + setFunc = function(text) db.text = text doStuff() end, + isMultiline = true, --boolean + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "Will need to reload the UI.", --(optional) + default = defaults.text, --(optional) + reference = "MyAddonEditbox" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 8 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("editbox", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local tinsert = table.insert + + +local function UpdateDisabled(control) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.editbox:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.editbox:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end + --control.editbox:SetEditEnabled(not disable) + control.editbox:SetMouseEnabled(not disable) +end + +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = control.data.default + control.data.setFunc(value) + control.editbox:SetText(value) + elseif value then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + else + value = control.data.getFunc() + control.editbox:SetText(value) + end +end + +local MIN_HEIGHT = 26 +local HALF_WIDTH_LINE_SPACING = 2 +function LAMCreateControl.editbox(parent, editboxData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, editboxData, controlName) + + local container = control.container + control.bg = wm:CreateControlFromVirtual(nil, container, "ZO_EditBackdrop") + local bg = control.bg + bg:SetAnchorFill() + + if editboxData.isMultiline then + control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditMultiLineForBackdrop") + control.editbox:SetHandler("OnMouseWheel", function(self, delta) + if self:HasFocus() then --only set focus to new spots if the editbox is currently in use + local cursorPos = self:GetCursorPosition() + local text = self:GetText() + local textLen = text:len() + local newPos + if delta > 0 then --scrolling up + local reverseText = text:reverse() + local revCursorPos = textLen - cursorPos + local revPos = reverseText:find("\n", revCursorPos+1) + newPos = revPos and textLen - revPos + else --scrolling down + newPos = text:find("\n", cursorPos+1) + end + if newPos then --if we found a new line, then scroll, otherwise don't + self:SetCursorPosition(newPos) + end + end + end) + else + control.editbox = wm:CreateControlFromVirtual(nil, bg, "ZO_DefaultEditForBackdrop") + end + local editbox = control.editbox + editbox:SetText(editboxData.getFunc()) + editbox:SetMaxInputChars(3000) + editbox:SetHandler("OnFocusLost", function(self) control:UpdateValue(false, self:GetText()) end) + editbox:SetHandler("OnEscape", function(self) self:LoseFocus() control:UpdateValue(false, self:GetText()) end) + editbox:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + editbox:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + + if not editboxData.isMultiline then + container:SetHeight(24) + else + local width = container:GetWidth() + local height = control.isHalfWidth and 74 or 100 + container:SetHeight(height) + editbox:SetDimensionConstraints(width, height, width, 500) + + if control.lineControl then + control.lineControl:SetHeight(MIN_HEIGHT + height + HALF_WIDTH_LINE_SPACING) + else + control:SetHeight(height) + end + end + + if editboxData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(TOPRIGHT, control.bg, TOPLEFT, -5, 0) + control.warning.data = {tooltipText = editboxData.warning} + end + + if editboxData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + control.UpdateValue = UpdateValue + control:UpdateValue() + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/header.lua b/Libs/LibAddonMenu-2.0/controls/header.lua new file mode 100644 index 0000000..266ac55 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/header.lua @@ -0,0 +1,45 @@ +--[[headerData = { + type = "header", + name = "My Header", + width = "full", --or "half" (optional) + reference = "MyAddonHeader" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 6 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("header", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local tinsert = table.insert + +local function UpdateValue(control) + control.header:SetText(control.data.name) +end + +local MIN_HEIGHT = 30 +function LAMCreateControl.header(parent, headerData, controlName) + local control = LAM.util.CreateBaseControl(parent, headerData, controlName) + local isHalfWidth = control.isHalfWidth + local width = control:GetWidth() + control:SetDimensions(isHalfWidth and width / 2 or width, MIN_HEIGHT) + + control.divider = wm:CreateControlFromVirtual(nil, control, "ZO_Options_Divider") + local divider = control.divider + divider:SetWidth(isHalfWidth and width / 2 or width) + divider:SetAnchor(TOPLEFT) + + control.header = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") + local header = control.header + header:SetAnchor(TOPLEFT, divider, BOTTOMLEFT) + header:SetAnchor(BOTTOMRIGHT) + header:SetText(headerData.name) + + control.UpdateValue = UpdateValue + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/iconpicker.lua b/Libs/LibAddonMenu-2.0/controls/iconpicker.lua new file mode 100644 index 0000000..1c34222 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/iconpicker.lua @@ -0,0 +1,441 @@ +--[[iconpickerData = { + type = "iconpicker", + name = "My Icon Picker", + tooltip = "Color Picker's tooltip text.", + choices = {"texture path 1", "texture path 2", "texture path 3"}, + choicesTooltips = {"icon tooltip 1", "icon tooltip 2", "icon tooltip 3"}, --(optional) + getFunc = function() return db.var end, + setFunc = function(var) db.var = var doStuff() end, + maxColumns = 5, --(optional) number of icons in one row + visibleRows = 4.5, --(optional) number of visible rows + iconSize = 28, --(optional) size of the icons + defaultColor = ZO_ColorDef:New("FFFFFF"), --(optional) default color of the icons + width = "full", --or "half" (optional) + beforeShow = function(control, iconPicker) return preventShow end, --(optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "Will need to reload the UI.", --(optional) + default = defaults.var, --(optional) + reference = "MyAddonIconPicker" --(optional) unique global reference to control +} ]] + +local widgetVersion = 2 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("iconpicker", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local tinsert = table.insert + +local IconPickerMenu = ZO_Object:Subclass() +local iconPicker +LAM.util.GetIconPickerMenu = function() + if not iconPicker then + iconPicker = IconPickerMenu:New("LAMIconPicker") + local sceneFragment = LAM:GetAddonSettingsFragment() + ZO_PreHook(sceneFragment, "OnHidden", function() + if not iconPicker.control:IsHidden() then + iconPicker:Clear() + end + end) + end + return iconPicker +end + +function IconPickerMenu:New(...) + local object = ZO_Object.New(self) + object:Initialize(...) + return object +end + +function IconPickerMenu:Initialize(name) + local control = wm:CreateTopLevelWindow(name) + control:SetDrawTier(DT_HIGH) + control:SetHidden(true) + self.control = control + + local scrollContainer = wm:CreateControlFromVirtual(name .. "ScrollContainer", control, "ZO_ScrollContainer") + -- control:SetDimensions(control.container:GetWidth(), height) -- adjust to icon size / col count + scrollContainer:SetAnchorFill() + ZO_Scroll_SetUseFadeGradient(scrollContainer, false) + ZO_Scroll_SetHideScrollbarOnDisable(scrollContainer, false) + ZO_VerticalScrollbarBase_OnMouseExit(scrollContainer:GetNamedChild("ScrollBar")) -- scrollbar initialization seems to be broken so we force it to update the correct alpha value + local scroll = GetControl(scrollContainer, "ScrollChild") + self.scroll = scroll + self.scrollContainer = scrollContainer + + local bg = wm:CreateControl(nil, scrollContainer, CT_BACKDROP) + bg:SetAnchor(TOPLEFT, scrollContainer, TOPLEFT, 0, -3) + bg:SetAnchor(BOTTOMRIGHT, scrollContainer, BOTTOMRIGHT, 2, 5) + bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16) + bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds") + bg:SetInsets(16, 16, -16, -16) + + local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE) + mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds") + mungeOverlay:SetDrawLevel(1) + mungeOverlay:SetAddressMode(TEX_MODE_WRAP) + mungeOverlay:SetAnchorFill() + + local mouseOver = wm:CreateControl(nil, scrollContainer, CT_TEXTURE) + mouseOver:SetDrawLevel(2) + mouseOver:SetTexture("EsoUI/Art/Buttons/minmax_mouseover.dds") + mouseOver:SetHidden(true) + + local function IconFactory(pool) + local icon = wm:CreateControl(name .. "Entry" .. pool:GetNextControlId(), scroll, CT_TEXTURE) + icon:SetMouseEnabled(true) + icon:SetDrawLevel(3) + icon:SetHandler("OnMouseEnter", function() + mouseOver:SetAnchor(TOPLEFT, icon, TOPLEFT, 0, 0) + mouseOver:SetAnchor(BOTTOMRIGHT, icon, BOTTOMRIGHT, 0, 0) + mouseOver:SetHidden(false) + if self.customOnMouseEnter then + self.customOnMouseEnter(icon) + else + self:OnMouseEnter(icon) + end + end) + icon:SetHandler("OnMouseExit", function() + mouseOver:ClearAnchors() + mouseOver:SetHidden(true) + if self.customOnMouseExit then + self.customOnMouseExit(icon) + else + self:OnMouseExit(icon) + end + end) + icon:SetHandler("OnMouseUp", function(control, ...) + PlaySound("Click") + icon.OnSelect(icon, icon.texture) + self:Clear() + end) + return icon + end + + local function ResetFunction(icon) + icon:ClearAnchors() + end + + self.iconPool = ZO_ObjectPool:New(IconFactory, ResetFunction) + self:SetMaxColumns(1) + self.icons = {} + self.color = ZO_DEFAULT_ENABLED_COLOR + + EVENT_MANAGER:RegisterForEvent(name .. "_OnGlobalMouseUp", EVENT_GLOBAL_MOUSE_UP, function() + if self.refCount ~= nil then + local moc = wm:GetMouseOverControl() + if(moc:GetOwningWindow() ~= control) then + self.refCount = self.refCount - 1 + if self.refCount <= 0 then + self:Clear() + end + end + end + end) +end + +function IconPickerMenu:OnMouseEnter(icon) + InitializeTooltip(InformationTooltip, icon, TOPLEFT, 0, 0, BOTTOMRIGHT) + SetTooltipText(InformationTooltip, LAM.util.GetTooltipText(icon.tooltip)) + InformationTooltipTopLevel:BringWindowToTop() +end + +function IconPickerMenu:OnMouseExit(icon) + ClearTooltip(InformationTooltip) +end + +function IconPickerMenu:SetMaxColumns(value) + self.maxCols = value ~= nil and value or 5 +end + +local DEFAULT_SIZE = 28 +function IconPickerMenu:SetIconSize(value) + local iconSize = DEFAULT_SIZE + if value ~= nil then iconSize = math.max(iconSize, value) end + self.iconSize = iconSize +end + +function IconPickerMenu:SetVisibleRows(value) + self.visibleRows = value ~= nil and value or 4.5 +end + +function IconPickerMenu:SetMouseHandlers(onEnter, onExit) + self.customOnMouseEnter = onEnter + self.customOnMouseExit = onExit +end + +function IconPickerMenu:UpdateDimensions() + local iconSize = self.iconSize + local width = iconSize * self.maxCols + 20 + local height = iconSize * self.visibleRows + self.control:SetDimensions(width, height) + + local icons = self.icons + for i = 1, #icons do + local icon = icons[i] + icon:SetDimensions(iconSize, iconSize) + end +end + +function IconPickerMenu:UpdateAnchors() + local iconSize = self.iconSize + local col, maxCols = 1, self.maxCols + local previousCol, previousRow + local scroll = self.scroll + local icons = self.icons + + for i = 1, #icons do + local icon = icons[i] + icon:ClearAnchors() + if i == 1 then + icon:SetAnchor(TOPLEFT, scroll, TOPLEFT, 0, 0) + previousRow = icon + elseif col == 1 then + icon:SetAnchor(TOPLEFT, previousRow, BOTTOMLEFT, 0, 0) + previousRow = icon + else + icon:SetAnchor(TOPLEFT, previousCol, TOPRIGHT, 0, 0) + end + previousCol = icon + col = col >= maxCols and 1 or col + 1 + end +end + +function IconPickerMenu:Clear() + self.icons = {} + self.iconPool:ReleaseAllObjects() + self.control:SetHidden(true) + self.color = ZO_DEFAULT_ENABLED_COLOR + self.refCount = nil + self.parent = nil + self.customOnMouseEnter = nil + self.customOnMouseExit = nil +end + +function IconPickerMenu:AddIcon(texturePath, callback, tooltip) + local icon, key = self.iconPool:AcquireObject() + icon:SetTexture(texturePath) + icon:SetColor(self.color:UnpackRGBA()) + icon.texture = texturePath + icon.tooltip = tooltip + icon.OnSelect = callback + self.icons[#self.icons + 1] = icon +end + +function IconPickerMenu:Show(parent) + if #self.icons == 0 then return false end + if not self.control:IsHidden() then self:Clear() return false end + self:UpdateDimensions() + self:UpdateAnchors() + + local control = self.control + control:ClearAnchors() + control:SetAnchor(TOPLEFT, parent, BOTTOMLEFT, 0, 8) + control:SetHidden(false) + control:BringWindowToTop() + self.parent = parent + self.refCount = 2 + + return true +end + +function IconPickerMenu:SetColor(color) + local icons = self.icons + self.color = color + for i = 1, #icons do + local icon = icons[i] + icon:SetColor(color:UnpackRGBA()) + end +end + +------------------------------------------------------------- + +local function UpdateChoices(control, choices, choicesTooltips) + local data = control.data + if not choices then + choices, choicesTooltips = data.choices, data.choicesTooltips + end + local addedChoices = {} + + local iconPicker = LAM.util.GetIconPickerMenu() + iconPicker:Clear() + for i = 1, #choices do + local texture = choices[i] + if not addedChoices[texture] then -- remove duplicates + iconPicker:AddIcon(choices[i], function(self, texture) + control.icon:SetTexture(texture) + data.setFunc(texture) + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + end, choicesTooltips[i]) + addedChoices[texture] = true + end + end +end + +local function IsDisabled(control) + if type(control.data.disabled) == "function" then + return control.data.disabled() + else + return control.data.disabled + end +end + +local function SetColor(control, color) + local icon = control.icon + if IsDisabled(control) then + icon:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + icon.color = color or control.data.defaultColor or ZO_DEFAULT_ENABLED_COLOR + icon:SetColor(icon.color:UnpackRGBA()) + end + + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then + iconPicker:SetColor(icon.color) + end +end + +local function UpdateDisabled(control) + local disable = IsDisabled(control) + + control.dropdown:SetMouseEnabled(not disable) + control.dropdownButton:SetEnabled(not disable) + + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then + iconPicker:Clear() + end + + SetColor(control) + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end +end + +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = control.data.default + control.data.setFunc(value) + control.icon:SetTexture(value) + elseif value then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + else + value = control.data.getFunc() + control.icon:SetTexture(value) + end +end + +local MIN_HEIGHT = 26 +local HALF_WIDTH_LINE_SPACING = 2 +local function SetIconSize(control, size) + local icon = control.icon + icon.size = size + icon:SetDimensions(size, size) + + local height = size + 4 + control.dropdown:SetDimensions(size + 20, height) + height = math.max(height, MIN_HEIGHT) + control.container:SetHeight(height) + if control.lineControl then + control.lineControl:SetHeight(MIN_HEIGHT + size + HALF_WIDTH_LINE_SPACING) + else + control:SetHeight(height) + end + + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container and not iconPicker.control:IsHidden() then + iconPicker:SetIconSize(size) + iconPicker:UpdateDimensions() + iconPicker:UpdateAnchors() + end +end + +function LAMCreateControl.iconpicker(parent, iconpickerData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, iconpickerData, controlName) + + local function ShowIconPicker() + local iconPicker = LAM.util.GetIconPickerMenu() + if iconPicker.parent == control.container then + iconPicker:Clear() + else + iconPicker:SetMaxColumns(iconpickerData.maxColumns) + iconPicker:SetVisibleRows(iconpickerData.visibleRows) + iconPicker:SetIconSize(control.icon.size) + UpdateChoices(control) + iconPicker:SetColor(control.icon.color) + if iconpickerData.beforeShow then + if iconpickerData.beforeShow(control, iconPicker) then + iconPicker:Clear() + return + end + end + iconPicker:Show(control.container) + end + end + + local iconSize = iconpickerData.iconSize ~= nil and iconpickerData.iconSize or DEFAULT_SIZE + control.dropdown = wm:CreateControl(nil, control.container, CT_CONTROL) + local dropdown = control.dropdown + dropdown:SetAnchor(LEFT, control.container, LEFT, 0, 0) + dropdown:SetMouseEnabled(true) + dropdown:SetHandler("OnMouseUp", ShowIconPicker) + dropdown:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + dropdown:SetHandler("OnMouseExit", function() ZO_Options_OnMouseExit(control) end) + + control.icon = wm:CreateControl(nil, dropdown, CT_TEXTURE) + local icon = control.icon + icon:SetAnchor(LEFT, dropdown, LEFT, 3, 0) + icon:SetDrawLevel(2) + + local dropdownButton = wm:CreateControlFromVirtual(nil, dropdown, "ZO_DropdownButton") + dropdownButton:SetDimensions(16, 16) + dropdownButton:SetHandler("OnClicked", ShowIconPicker) + dropdownButton:SetAnchor(RIGHT, dropdown, RIGHT, -3, 0) + control.dropdownButton = dropdownButton + + control.bg = wm:CreateControl(nil, dropdown, CT_BACKDROP) + local bg = control.bg + bg:SetAnchor(TOPLEFT, dropdown, TOPLEFT, 0, -3) + bg:SetAnchor(BOTTOMRIGHT, dropdown, BOTTOMRIGHT, 2, 5) + bg:SetEdgeTexture("EsoUI/Art/Tooltips/UI-Border.dds", 128, 16) + bg:SetCenterTexture("EsoUI/Art/Tooltips/UI-TooltipCenter.dds") + bg:SetInsets(16, 16, -16, -16) + local mungeOverlay = wm:CreateControl(nil, bg, CT_TEXTURE) + mungeOverlay:SetTexture("EsoUI/Art/Tooltips/munge_overlay.dds") + mungeOverlay:SetDrawLevel(1) + mungeOverlay:SetAddressMode(TEX_MODE_WRAP) + mungeOverlay:SetAnchorFill() + + if iconpickerData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, control.container, LEFT, -5, 0) + control.warning.data = {tooltipText = iconpickerData.warning} + end + + if iconpickerData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + + control.UpdateChoices = UpdateChoices + control.UpdateValue = UpdateValue + control:UpdateValue() + control.SetColor = SetColor + control:SetColor() + control.SetIconSize = SetIconSize + control:SetIconSize(iconSize) + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/panel.lua b/Libs/LibAddonMenu-2.0/controls/panel.lua new file mode 100644 index 0000000..0d698c7 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/panel.lua @@ -0,0 +1,132 @@ +--[[panelData = { + type = "panel", + name = "Window Title", + displayName = "My Longer Window Title", --(optional) (can be useful for long addon names or if you want to colorize it) + author = "Seerah", --(optional) + version = "2.0", --(optional) + keywords = "settings", --(optional) additional keywords for search filter (it looks for matches in name..keywords..author) + slashCommand = "/myaddon", --(optional) will register a keybind to open to this panel (don't forget to include the slash!) + registerForRefresh = true, --boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown) + registerForDefaults = true, --boolean (optional) (will set all options controls back to default values) + resetFunc = function() print("defaults reset") end, --(optional) custom function to run after settings are reset to defaults +} ]] + + +local widgetVersion = 9 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("panel", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER + +local function RefreshPanel(control) + local panel = control.panel or control --callback can be fired by a single control or by the panel showing + local panelControls = panel.controlsToRefresh + + for i = 1, #panelControls do + local updateControl = panelControls[i] + if updateControl ~= control then + if updateControl.UpdateValue then + updateControl:UpdateValue() + end + if updateControl.UpdateDisabled then + updateControl:UpdateDisabled() + end + end + end +end + +local function ForceDefaults(panel) + local panelControls = panel.controlsToRefresh + + for i = 1, #panelControls do + local updateControl = panelControls[i] + if updateControl.UpdateValue and updateControl.data.default ~= nil then + updateControl:UpdateValue(true) + end + end + + if panel.data.resetFunc then + panel.data.resetFunc() + end + + cm:FireCallbacks("LAM-RefreshPanel", panel) +end +ESO_Dialogs["LAM_DEFAULTS"] = { + title = { + text = SI_INTERFACE_OPTIONS_RESET_TO_DEFAULT_TOOLTIP, + }, + mainText = { + text = SI_OPTIONS_RESET_PROMPT, + align = TEXT_ALIGN_CENTER, + }, + buttons = { + [1] = { + text = SI_OPTIONS_RESET, + callback = function(dialog) ForceDefaults(dialog.data[1]) end, + }, + [2] = { + text = SI_DIALOG_CANCEL, + }, + }, +} + +local callbackRegistered = false +LAMCreateControl.scrollCount = LAMCreateControl.scrollCount or 1 +function LAMCreateControl.panel(parent, panelData, controlName) + local control = wm:CreateControl(controlName, parent, CT_CONTROL) + + control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") + local label = control.label + label:SetAnchor(TOPLEFT, control, TOPLEFT, 0, 4) + label:SetText(panelData.displayName or panelData.name) + + if panelData.author or panelData.version then + control.info = wm:CreateControl(nil, control, CT_LABEL) + local info = control.info + info:SetFont("$(CHAT_FONT)|14|soft-shadow-thin") + info:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, -2) + if panelData.author and panelData.version then + info:SetText(string.format("Version: %s - %s: %s", panelData.version, GetString(SI_ADDON_MANAGER_AUTHOR), panelData.author)) + elseif panelData.author then + info:SetText(string.format("%s: %s", GetString(SI_ADDON_MANAGER_AUTHOR), panelData.author)) + else + info:SetText("Version: "..panelData.version) + end + end + + control.container = wm:CreateControlFromVirtual("LAMAddonPanelContainer"..LAMCreateControl.scrollCount, control, "ZO_ScrollContainer") + LAMCreateControl.scrollCount = LAMCreateControl.scrollCount + 1 + local container = control.container + container:SetAnchor(TOPLEFT, control.info or label, BOTTOMLEFT, 0, 20) + container:SetAnchor(BOTTOMRIGHT, control, BOTTOMRIGHT, -3, -3) + control.scroll = GetControl(control.container, "ScrollChild") + control.scroll:SetResizeToFitPadding(0, 20) + + if panelData.registerForDefaults then + control.defaultButton = wm:CreateControlFromVirtual(nil, control, "ZO_DefaultTextButton") + local defaultButton = control.defaultButton + defaultButton:SetFont("ZoFontDialogKeybindDescription") + defaultButton:SetHorizontalAlignment(TEXT_ALIGN_LEFT) + --defaultButton:SetText("Reset To Defaults") + defaultButton:SetText(GetString(SI_OPTIONS_DEFAULTS)) + defaultButton:SetDimensions(200, 30) + defaultButton:SetAnchor(TOPLEFT, control, BOTTOMLEFT, 0, 2) + defaultButton:SetHandler("OnClicked", function() + ZO_Dialogs_ShowDialog("LAM_DEFAULTS", {control}) + end) + end + + if panelData.registerForRefresh and not callbackRegistered then --don't want to register our callback more than once + cm:RegisterCallback("LAM-RefreshPanel", RefreshPanel) + callbackRegistered = true + end + + control.data = panelData + control.controlsToRefresh = {} + + return control +end + + +-- vi: noexpandtab diff --git a/Libs/LibAddonMenu-2.0/controls/slider.lua b/Libs/LibAddonMenu-2.0/controls/slider.lua new file mode 100644 index 0000000..93eafd8 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/slider.lua @@ -0,0 +1,157 @@ +--[[sliderData = { + type = "slider", + name = "My Slider", + tooltip = "Slider's tooltip text.", + min = 0, + max = 20, + step = 1, --(optional) + getFunc = function() return db.var end, + setFunc = function(value) db.var = value doStuff() end, + width = "full", --or "half" (optional) + disabled = function() return db.someBooleanSetting end, --or boolean (optional) + warning = "Will need to reload the UI.", --(optional) + default = defaults.var, --(optional) + reference = "MyAddonSlider" --(optional) unique global reference to control +} ]] + + +local widgetVersion = 7 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("slider", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local cm = CALLBACK_MANAGER +local round = zo_round +local strformat = string.format +local tinsert = table.insert + +local function UpdateDisabled(control) + local disable + if type(control.data.disabled) == "function" then + disable = control.data.disabled() + else + disable = control.data.disabled + end + + control.slider:SetEnabled(not disable) + control.slidervalue:SetEditEnabled(not disable) + if disable then + control.label:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.minText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.maxText:SetColor(ZO_DEFAULT_DISABLED_COLOR:UnpackRGBA()) + control.slidervalue:SetColor(ZO_DEFAULT_DISABLED_MOUSEOVER_COLOR:UnpackRGBA()) + else + control.label:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.minText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.maxText:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + control.slidervalue:SetColor(ZO_DEFAULT_ENABLED_COLOR:UnpackRGBA()) + end +end + +local function UpdateValue(control, forceDefault, value) + if forceDefault then --if we are forcing defaults + value = control.data.default + control.data.setFunc(value) + elseif value and value >= control.data.min and value <= control.data.max then + control.data.setFunc(value) + --after setting this value, let's refresh the others to see if any should be disabled or have their settings changed + if control.panel.data.registerForRefresh then + cm:FireCallbacks("LAM-RefreshPanel", control) + end + else + value = control.data.getFunc() + end + + control.slider:SetValue(value) + control.slidervalue:SetText(value) +end + + +function LAMCreateControl.slider(parent, sliderData, controlName) + local control = LAM.util.CreateLabelAndContainerControl(parent, sliderData, controlName) + + --skipping creating the backdrop... Is this the actual slider texture? + control.slider = wm:CreateControl(nil, control.container, CT_SLIDER) + local slider = control.slider + slider:SetAnchor(TOPLEFT) + slider:SetAnchor(TOPRIGHT) + slider:SetHeight(14) + slider:SetMouseEnabled(true) + slider:SetOrientation(ORIENTATION_HORIZONTAL) + --put nil for highlighted texture file path, and what look to be texture coords + slider:SetThumbTexture("EsoUI\\Art\\Miscellaneous\\scrollbox_elevator.dds", "EsoUI\\Art\\Miscellaneous\\scrollbox_elevator_disabled.dds", nil, 8, 16) + local minValue = sliderData.min + local maxValue = sliderData.max + slider:SetMinMax(minValue, maxValue) + slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseEnter(control) end) + slider:SetHandler("OnMouseEnter", function() ZO_Options_OnMouseExit(control) end) + + slider.bg = wm:CreateControl(nil, slider, CT_BACKDROP) + local bg = slider.bg + bg:SetCenterColor(0, 0, 0) + bg:SetAnchor(TOPLEFT, slider, TOPLEFT, 0, 4) + bg:SetAnchor(BOTTOMRIGHT, slider, BOTTOMRIGHT, 0, -4) + bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-SliderBackdrop.dds", 32, 4) + + control.minText = wm:CreateControl(nil, slider, CT_LABEL) + local minText = control.minText + minText:SetFont("ZoFontGameSmall") + minText:SetAnchor(TOPLEFT, slider, BOTTOMLEFT) + minText:SetText(sliderData.min) + + control.maxText = wm:CreateControl(nil, slider, CT_LABEL) + local maxText = control.maxText + maxText:SetFont("ZoFontGameSmall") + maxText:SetAnchor(TOPRIGHT, slider, BOTTOMRIGHT) + maxText:SetText(sliderData.max) + + control.slidervalueBG = wm:CreateControlFromVirtual(nil, slider, "ZO_EditBackdrop") + control.slidervalueBG:SetDimensions(50, 16) + control.slidervalueBG:SetAnchor(TOP, slider, BOTTOM, 0, 0) + control.slidervalue = wm:CreateControlFromVirtual(nil, control.slidervalueBG, "ZO_DefaultEditForBackdrop") + local slidervalue = control.slidervalue + slidervalue:ClearAnchors() + slidervalue:SetAnchor(TOPLEFT, control.slidervalueBG, TOPLEFT, 3, 1) + slidervalue:SetAnchor(BOTTOMRIGHT, control.slidervalueBG, BOTTOMRIGHT, -3, -1) + slidervalue:SetTextType(TEXT_TYPE_NUMERIC) + slidervalue:SetFont("ZoFontGameSmall") + slidervalue:SetHandler("OnEscape", function(self) + self:LoseFocus() + control:UpdateValue() + end) + slidervalue:SetHandler("OnEnter", function(self) + self:LoseFocus() + control:UpdateValue(false, tonumber(self:GetText())) + end) + + local range = maxValue - minValue + slider:SetValueStep(sliderData.step or 1) + slider:SetHandler("OnValueChanged", function(self, value, eventReason) + if eventReason == EVENT_REASON_SOFTWARE then return end + self:SetValue(value) --do we actually need this line? + slidervalue:SetText(value) + end) + slider:SetHandler("OnSliderReleased", function(self, value) + --sliderData.setFunc(value) + control:UpdateValue(false, value) --does this work here instead? + end) + + if sliderData.warning then + control.warning = wm:CreateControlFromVirtual(nil, control, "ZO_Options_WarningIcon") + control.warning:SetAnchor(RIGHT, slider, LEFT, -5, 0) + control.warning.data = {tooltipText = sliderData.warning} + end + + if sliderData.disabled then + control.UpdateDisabled = UpdateDisabled + control:UpdateDisabled() + end + control.UpdateValue = UpdateValue + control:UpdateValue() + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibAddonMenu-2.0/controls/submenu.lua b/Libs/LibAddonMenu-2.0/controls/submenu.lua new file mode 100644 index 0000000..8022809 --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/submenu.lua @@ -0,0 +1,112 @@ +--[[submenuData = { + type = "submenu", + name = "Submenu Title", + tooltip = "My submenu tooltip", --(optional) + controls = {sliderData, buttonData} --(optional) used by LAM + reference = "MyAddonSubmenu" --(optional) unique global reference to control +} ]] + +local widgetVersion = 9 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("submenu", widgetVersion) then return end + +local wm = WINDOW_MANAGER +local am = ANIMATION_MANAGER +local tinsert = table.insert + + +local function UpdateValue(control) + control.label:SetText(control.data.name) + if control.data.tooltip then + control.label.data.tooltipText = LAM.util.GetTooltipText(control.data.tooltip) + end +end + +local function AnimateSubmenu(clicked) + local control = clicked:GetParent() + control.open = not control.open + + if control.open then + control.animation:PlayFromStart() + else + control.animation:PlayFromEnd() + end +end + +function LAMCreateControl.submenu(parent, submenuData, controlName) + local width = parent:GetWidth() - 45 + local control = wm:CreateControl(controlName or submenuData.reference, parent.scroll or parent, CT_CONTROL) + control.panel = parent + control.data = submenuData + + control.label = wm:CreateControlFromVirtual(nil, control, "ZO_Options_SectionTitleLabel") + local label = control.label + label:SetAnchor(TOPLEFT, control, TOPLEFT, 5, 5) + label:SetDimensions(width, 30) + label:SetWrapMode(TEXT_WRAP_MODE_ELLIPSIS) + label:SetText(submenuData.name) + label:SetMouseEnabled(true) + if submenuData.tooltip then + label.data = {tooltipText = LAM.util.GetTooltipText(submenuData.tooltip)} + label:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + label:SetHandler("OnMouseExit", ZO_Options_OnMouseExit) + end + + control.scroll = wm:CreateControl(nil, control, CT_SCROLL) + local scroll = control.scroll + scroll:SetParent(control) + scroll:SetAnchor(TOPLEFT, label, BOTTOMLEFT, 0, 10) + scroll:SetDimensionConstraints(width + 5, 0, width + 5, 2500) + + control.bg = wm:CreateControl(nil, label, CT_BACKDROP) + local bg = control.bg + bg:SetAnchor(TOPLEFT, label, TOPLEFT, -5, -5) + bg:SetAnchor(BOTTOMRIGHT, scroll, BOTTOMRIGHT, -7, 0) + bg:SetEdgeTexture("EsoUI\\Art\\Tooltips\\UI-Border.dds", 128, 16) + bg:SetCenterTexture("EsoUI\\Art\\Tooltips\\UI-TooltipCenter.dds") + bg:SetInsets(16, 16, -16, -16) + + control.arrow = wm:CreateControl(nil, bg, CT_TEXTURE) + local arrow = control.arrow + arrow:SetDimensions(28, 28) + arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") --list_sortup for the other way + arrow:SetAnchor(TOPRIGHT, bg, TOPRIGHT, -5, 5) + + --figure out the cool animation later... + control.animation = am:CreateTimeline() + local animation = control.animation + animation:SetPlaybackType(ANIMATION_SIZE, 0) --2nd arg = loop count + + control:SetResizeToFitDescendents(true) + control.open = false + label:SetHandler("OnMouseUp", AnimateSubmenu) + animation:SetHandler("OnStop", function(self, completedPlaying) + scroll:SetResizeToFitDescendents(control.open) + if control.open then + control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortup.dds") + scroll:SetResizeToFitPadding(5, 20) + else + control.arrow:SetTexture("EsoUI\\Art\\Miscellaneous\\list_sortdown.dds") + scroll:SetResizeToFitPadding(5, 0) + scroll:SetHeight(0) + end + end) + + --small strip at the bottom of the submenu that you can click to close it + control.btmToggle = wm:CreateControl(nil, control, CT_TEXTURE) + local btmToggle = control.btmToggle + btmToggle:SetMouseEnabled(true) + btmToggle:SetAnchor(BOTTOMLEFT, control.scroll, BOTTOMLEFT) + btmToggle:SetAnchor(BOTTOMRIGHT, control.scroll, BOTTOMRIGHT) + btmToggle:SetHeight(15) + btmToggle:SetAlpha(0) + btmToggle:SetHandler("OnMouseUp", AnimateSubmenu) + + control.UpdateValue = UpdateValue + + if control.panel.data.registerForRefresh or control.panel.data.registerForDefaults then --if our parent window wants to refresh controls, then add this to the list + tinsert(control.panel.controlsToRefresh, control) + end + + return control +end diff --git a/Libs/LibAddonMenu-2.0/controls/texture.lua b/Libs/LibAddonMenu-2.0/controls/texture.lua new file mode 100644 index 0000000..c6ef95d --- /dev/null +++ b/Libs/LibAddonMenu-2.0/controls/texture.lua @@ -0,0 +1,45 @@ +--[[textureData = { + type = "texture", + image = "file/path.dds", + imageWidth = 64, --max of 250 for half width, 510 for full + imageHeight = 32, --max of 100 + tooltip = "Image's tooltip text.", --(optional) + width = "full", --or "half" (optional) + reference = "MyAddonTexture" --(optional) unique global reference to control +} ]] + +--add texture coords support? + +local widgetVersion = 7 +local LAM = LibStub("LibAddonMenu-2.0") +if not LAM:RegisterWidget("texture", widgetVersion) then return end + +local wm = WINDOW_MANAGER + +local MIN_HEIGHT = 26 +function LAMCreateControl.texture(parent, textureData, controlName) + local control = LAM.util.CreateBaseControl(parent, textureData, controlName) + local width = control:GetWidth() + control:SetResizeToFitDescendents(true) + + if control.isHalfWidth then --note these restrictions + control:SetDimensionConstraints(width / 2, MIN_HEIGHT, width / 2, MIN_HEIGHT * 4) + else + control:SetDimensionConstraints(width, MIN_HEIGHT, width, MIN_HEIGHT * 4) + end + + control.texture = wm:CreateControl(nil, control, CT_TEXTURE) + local texture = control.texture + texture:SetAnchor(CENTER) + texture:SetDimensions(textureData.imageWidth, textureData.imageHeight) + texture:SetTexture(textureData.image) + + if textureData.tooltip then + texture:SetMouseEnabled(true) + texture.data = {tooltipText = LAM.util.GetTooltipText(textureData.tooltip)} + texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseEnter) + texture:SetHandler("OnMouseEnter", ZO_Options_OnMouseExit) + end + + return control +end \ No newline at end of file diff --git a/Libs/LibStub/LibStub.lua b/Libs/LibStub/LibStub.lua new file mode 100644 index 0000000..41293bc --- /dev/null +++ b/Libs/LibStub/LibStub.lua @@ -0,0 +1,36 @@ +-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info +-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke +-- LibStub developed for World of Warcraft by above members of the WowAce community. +-- Ported to Elder Scrolls Online by Seerah + +local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 3 +local LibStub = _G[LIBSTUB_MAJOR] + +local strformat = string.format +if not LibStub or LibStub.minor < LIBSTUB_MINOR then + LibStub = LibStub or {libs = {}, minors = {} } + _G[LIBSTUB_MAJOR] = LibStub + LibStub.minor = LIBSTUB_MINOR + + function LibStub:NewLibrary(major, minor) + assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") + if type(minor) ~= "number" then + minor = assert(tonumber(zo_strmatch(minor, "%d+%.?%d*")), "Minor version must either be a number or contain a number.") + end + + local oldminor = self.minors[major] + if oldminor and oldminor >= minor then return nil end + self.minors[major], self.libs[major] = minor, self.libs[major] or {} + return self.libs[major], oldminor + end + + function LibStub:GetLibrary(major, silent) + if not self.libs[major] and not silent then + error(strformat("Cannot find a library instance of %q.", tostring(major)), 2) + end + return self.libs[major], self.minors[major] + end + + function LibStub:IterateLibraries() return pairs(self.libs) end + setmetatable(LibStub, { __call = LibStub.GetLibrary }) +end diff --git a/Libs/exampleoptions.lua b/Libs/exampleoptions.lua new file mode 100644 index 0000000..4059b9d --- /dev/null +++ b/Libs/exampleoptions.lua @@ -0,0 +1,119 @@ +local panelData = { + type = "panel", + name = "Window Title", + displayName = "Longer Window Title", + author = "Seerah", + version = "1.3", + slashCommand = "/myaddon", --(optional) will register a keybind to open to this panel + registerForRefresh = true, --boolean (optional) (will refresh all options controls when a setting is changed and when the panel is shown) + registerForDefaults = true, --boolean (optional) (will set all options controls back to default values) +} + +local optionsTable = { + [1] = { + type = "header", + name = "My Header", + width = "full", --or "half" (optional) + }, + [2] = { + type = "description", + --title = "My Title", --(optional) + title = nil, --(optional) + text = "My description text to display. blah blah blah blah blah blah blah - even more sample text!!", + width = "full", --or "half" (optional) + }, + [3] = { + type = "dropdown", + name = "My Dropdown", + tooltip = "Dropdown's tooltip text.", + choices = {"table", "of", "choices"}, + getFunc = function() return "of" end, + setFunc = function(var) print(var) end, + width = "half", --or "half" (optional) + warning = "Will need to reload the UI.", --(optional) + }, + [4] = { + type = "dropdown", + name = "My Dropdown", + tooltip = "Dropdown's tooltip text.", + choices = {"table", "of", "choices"}, + getFunc = function() return "of" end, + setFunc = function(var) print(var) end, + width = "half", --or "half" (optional) + warning = "Will need to reload the UI.", --(optional) + }, + [5] = { + type = "slider", + name = "My Slider", + tooltip = "Slider's tooltip text.", + min = 0, + max = 20, + step = 1, --(optional) + getFunc = function() return 3 end, + setFunc = function(value) d(value) end, + width = "half", --or "half" (optional) + default = 5, --(optional) + }, + [6] = { + type = "button", + name = "My Button", + tooltip = "Button's tooltip text.", + func = function() d("button pressed!") end, + width = "half", --or "half" (optional) + warning = "Will need to reload the UI.", --(optional) + }, + [7] = { + type = "submenu", + name = "Submenu Title", + tooltip = "My submenu tooltip", --(optional) + controls = { + [1] = { + type = "checkbox", + name = "My Checkbox", + tooltip = "Checkbox's tooltip text.", + getFunc = function() return true end, + setFunc = function(value) d(value) end, + width = "half", --or "half" (optional) + warning = "Will need to reload the UI.", --(optional) + }, + [2] = { + type = "colorpicker", + name = "My Color Picker", + tooltip = "Color Picker's tooltip text.", + getFunc = function() return 1, 0, 0, 1 end, --(alpha is optional) + setFunc = function(r,g,b,a) print(r, g, b, a) end, --(alpha is optional) + width = "half", --or "half" (optional) + warning = "warning text", + }, + [3] = { + type = "editbox", + name = "My Editbox", + tooltip = "Editbox's tooltip text.", + getFunc = function() return "this is some text" end, + setFunc = function(text) print(text) end, + isMultiline = false, --boolean + width = "half", --or "half" (optional) + warning = "Will need to reload the UI.", --(optional) + default = "", --(optional) + }, + }, + }, + [8] = { + type = "custom", + reference = "MyAddonCustomControl", --unique name for your control to use as reference + refreshFunc = function(customControl) end, --(optional) function to call when panel/controls refresh + width = "half", --or "half" (optional) + }, + [9] = { + type = "texture", + image = "EsoUI\\Art\\ActionBar\\abilityframe64_up.dds", + imageWidth = 64, --max of 250 for half width, 510 for full + imageHeight = 64, --max of 100 + tooltip = "Image's tooltip text.", --(optional) + width = "half", --or "half" (optional) + }, +} + +local LAM = LibStub("LibAddonMenu-2.0") +LAM:RegisterAddonPanel("MyAddon", panelData) +LAM:RegisterOptionControls("MyAddon", optionsTable) \ No newline at end of file