From 55fd85bca0117eadf77b502c3eebe9a33fd5d470 Mon Sep 17 00:00:00 2001 From: Kkthnx <40672673+Kkthnx@users.noreply.github.com> Date: Sun, 26 Jan 2025 14:24:10 -0500 Subject: [PATCH] Added options to screenshot on level up You can screenshot per 10 levels or every level. You can enabled or disable this per the config which is disabled by default. Fixed missing arg in PLAYER_LEVEL_UP. Updated Dashi! --- LevelUpFX/Config/Settings.lua | 15 ++ LevelUpFX/LevelUpFX.lua | 29 ++- LevelUpFX/LevelUpFX_Classic.toc | 2 +- LevelUpFX/Libs/Dashi/modules/build.lua | 8 +- LevelUpFX/Libs/Dashi/modules/defer.lua | 23 +- LevelUpFX/Libs/Dashi/modules/event.lua | 134 ++++++------ LevelUpFX/Libs/Dashi/modules/input.lua | 14 +- LevelUpFX/Libs/Dashi/modules/library.lua | 66 +++++- LevelUpFX/Libs/Dashi/modules/load.lua | 9 +- LevelUpFX/Libs/Dashi/modules/locale.lua | 16 +- LevelUpFX/Libs/Dashi/modules/misc.lua | 59 +++--- LevelUpFX/Libs/Dashi/modules/object.lua | 15 +- LevelUpFX/Libs/Dashi/modules/output.lua | 18 +- LevelUpFX/Libs/Dashi/modules/settings.lua | 242 ++++++++++++---------- LevelUpFX/Libs/Dashi/modules/widgets.lua | 44 ++-- 15 files changed, 419 insertions(+), 275 deletions(-) diff --git a/LevelUpFX/Config/Settings.lua b/LevelUpFX/Config/Settings.lua index 9c5da74..f37381a 100644 --- a/LevelUpFX/Config/Settings.lua +++ b/LevelUpFX/Config/Settings.lua @@ -15,6 +15,21 @@ namespace:RegisterSettings("LevelUpFXDB", { tooltip = "50% chance to perform the 'CHEER' emote when you level up.", default = false, }, + { + key = "screenShotOnLevelUp", + type = "toggle", + title = "Screenshot on Level Up", + tooltip = "Automatically take a screenshot one second after leveling up.", + default = false, + }, + { + key = "screenShotEveryLevel", + type = "toggle", + title = "Screenshot Every Level", + tooltip = "Take a screenshot for every level up, not just at specific levels.", + default = false, + requires = "screenShotOnLevelUp", + }, { key = "chatEmoteOnLevelUp", type = "toggle", diff --git a/LevelUpFX/LevelUpFX.lua b/LevelUpFX/LevelUpFX.lua index 6d3bb00..6ee1738 100644 --- a/LevelUpFX/LevelUpFX.lua +++ b/LevelUpFX/LevelUpFX.lua @@ -42,7 +42,7 @@ local function ShowLevelUpMessage(level, statGains, isMoving) background:SetPoint("BOTTOM") background:SetSize(326, 103) background:SetTexCoord(0.00195313, 0.63867188, 0.03710938, 0.23828125) - background:SetVertexColor(1, 1, 1, 0.7) + background:SetVertexColor(1, 1, 1, 0.6) -- Top gold bar local topBar = frame:CreateTexture(nil, "ARTWORK") @@ -129,6 +129,30 @@ local function ShowLevelUpMessage(level, statGains, isMoving) frame:Show() end + -- Add screenshot functionality here + if namespace:GetOption("screenShotOnLevelUp") then + if not namespace.ScreenShotFrame then + -- Create the screenshot frame if it doesn't exist + namespace.ScreenShotFrame = CreateFrame("Frame") + namespace.ScreenShotFrame:Hide() + namespace.ScreenShotFrame:SetScript("OnUpdate", function(self, elapsed) + self.delay = self.delay - elapsed + if self.delay < 0 then + Screenshot() + self:Hide() + end + end) + end + + -- Check if we should take a screenshot for this level + local screenshotLevels = { [10] = true, [20] = true, [30] = true, [40] = true, [50] = true, [60] = true } + + if screenshotLevels[level] or namespace:GetOption("screenShotEveryLevel") then + namespace.ScreenShotFrame.delay = 1 + namespace.ScreenShotFrame:Show() + end + end + -- Perform the "CHEER" emote if enabled and not moving if not isMoving and namespace:GetOption("cheerOnLevelUp") then if math.random() < 0.5 then @@ -142,8 +166,7 @@ local function ShowLevelUpMessage(level, statGains, isMoving) end end --- Event handler -namespace:RegisterEvent("PLAYER_LEVEL_UP", function(_, level, _, _, _, strengthDelta, agilityDelta, staminaDelta, intellectDelta, spiritDelta) +namespace:RegisterEvent("PLAYER_LEVEL_UP", function(_, level, _, _, _, _, strengthDelta, agilityDelta, staminaDelta, intellectDelta, spiritDelta) local statGains = { Strength = strengthDelta or 0, Agility = agilityDelta or 0, diff --git a/LevelUpFX/LevelUpFX_Classic.toc b/LevelUpFX/LevelUpFX_Classic.toc index bd2ab84..07b8264 100644 --- a/LevelUpFX/LevelUpFX_Classic.toc +++ b/LevelUpFX/LevelUpFX_Classic.toc @@ -3,7 +3,7 @@ ## Title: |cff5bc0beLevelUpFX|r ## Notes: Celebrate your level-ups with style! Displays a animated message and stats gained whenever you level up in World of Warcraft Classic Era.|n|n|cff669DFFDeveloper|cffffffff:|r |nJosh "|CFF7b8489Kkthnx|r" Russell|n|n|cff009cdePayPal|r|cffffffff:|r |n|cffffffffwww.paypal.me/KkthnxTV|r|n|n|cfff96854Patreon|r|cffffffff:|r |n|cffffffffwww.patreon.com/Kkthnx|r ## Author: Josh "Kkthnx" Russell, DaleHuntGB -## Version: 1.0.2 +## Version: 1.0.3 ## IconTexture: 236567 ## SavedVariables: LevelUpFXDB ## X-Support: Classic diff --git a/LevelUpFX/Libs/Dashi/modules/build.lua b/LevelUpFX/Libs/Dashi/modules/build.lua index 529a804..17343a1 100644 --- a/LevelUpFX/Libs/Dashi/modules/build.lua +++ b/LevelUpFX/Libs/Dashi/modules/build.lua @@ -1,20 +1,20 @@ local _, addon = ... ---[[ namespace:IsRetail() +--[[ namespace:IsRetail() ![](https://img.shields.io/badge/function-blue) Checks if the current client is running the "retail" version. --]] function addon:IsRetail() return WOW_PROJECT_ID == WOW_PROJECT_MAINLINE end ---[[ namespace:IsClassicEra() +--[[ namespace:IsClassicEra() ![](https://img.shields.io/badge/function-blue) Checks if the current client is running the "classic era" version (e.g. vanilla). --]] function addon:IsClassicEra() return WOW_PROJECT_ID == WOW_PROJECT_CLASSIC end ---[[ namespace:IsClassic() +--[[ namespace:IsClassic() ![](https://img.shields.io/badge/function-blue) Checks if the current client is running the "classic" version. --]] function addon:IsClassic() @@ -24,7 +24,7 @@ function addon:IsClassic() end local _, buildVersion, _, interfaceVersion = GetBuildInfo() ---[[ namespace:HasBuild(_buildNumber_[, _interfaceVersion_]) +--[[ namespace:HasBuild(_buildNumber_[, _interfaceVersion_]) ![](https://img.shields.io/badge/function-blue) Checks if the current client is running a build equal to or newer than the specified. Optionally also check against the interface version. --]] diff --git a/LevelUpFX/Libs/Dashi/modules/defer.lua b/LevelUpFX/Libs/Dashi/modules/defer.lua index 6ce8c6e..5795156 100644 --- a/LevelUpFX/Libs/Dashi/modules/defer.lua +++ b/LevelUpFX/Libs/Dashi/modules/defer.lua @@ -23,28 +23,27 @@ end local function defer(info) table.insert(queue, info) - if not addon:IsEventRegistered('PLAYER_REGEN_ENABLED', iterate) then - addon:RegisterEvent('PLAYER_REGEN_ENABLED', iterate) + if not addon:IsEventRegistered("PLAYER_REGEN_ENABLED", iterate) then + addon:RegisterEvent("PLAYER_REGEN_ENABLED", iterate) end end - ---[[ namespace:Defer(_callback_[, _..._]) +--[[ namespace:Defer(_callback_[, _..._]) ![](https://img.shields.io/badge/function-blue) Defers a function `callback` (with optional arguments) until after combat ends. Callback can be the global name of a function. Triggers immediately if player is not in combat. --]] function addon:Defer(callback, ...) - if type(callback) == 'string' then + if type(callback) == "string" then callback = _G[callback] end - addon:ArgCheck(callback, 1, 'function') + addon:ArgCheck(callback, 1, "function") if InCombatLockdown() then defer({ callback = callback, - args = {...}, + args = { ... }, }) else local successful, ret = pcall(callback, ...) @@ -54,20 +53,20 @@ function addon:Defer(callback, ...) end end ---[[ namespace:DeferMethod(_object_, _method_[, _..._]) +--[[ namespace:DeferMethod(_object_, _method_[, _..._]) ![](https://img.shields.io/badge/function-blue) Defers a `method` on `object` (with optional arguments) until after combat ends. Triggers immediately if player is not in combat. --]] function addon:DeferMethod(object, method, ...) - addon:ArgCheck(object, 1, 'table') - addon:ArgCheck(method, 2, 'string') - addon:ArgCheck(object[method], 2, 'function') + addon:ArgCheck(object, 1, "table") + addon:ArgCheck(method, 2, "string") + addon:ArgCheck(object[method], 2, "function") if InCombatLockdown() then defer({ object = object, method = method, - args = {...}, + args = { ... }, }) else local successful, ret = pcall(object[method], object, ...) diff --git a/LevelUpFX/Libs/Dashi/modules/event.lua b/LevelUpFX/Libs/Dashi/modules/event.lua index d6d5654..510e230 100644 --- a/LevelUpFX/Libs/Dashi/modules/event.lua +++ b/LevelUpFX/Libs/Dashi/modules/event.lua @@ -1,9 +1,9 @@ local addonName, addon = ... ---[[ namespace.eventMixin +--[[ namespace.eventMixin ![](https://img.shields.io/badge/object-teal) A multi-purpose [event](https://warcraft.wiki.gg/wiki/Events)-[mixin](https://en.wikipedia.org/wiki/Mixin). -These methods are also available as methods directly on `namespace`, e.g: +These methods are mixed into `namespace`, and thus are available directly, e.g: ```lua namespace:RegisterEvent('BAG_UPDATE', function(self, ...) @@ -12,14 +12,14 @@ end) ``` --]] -local eventHandler = CreateFrame('Frame') +local eventHandler = CreateFrame("Frame") local callbacks = {} local IsEventValid if addon:IsRetail() then IsEventValid = C_EventUtils.IsEventValid else - local eventValidator = CreateFrame('Frame') + local eventValidator = CreateFrame("Frame") function IsEventValid(event) local isValid = pcall(eventValidator.RegisterEvent, eventValidator, event) if isValid then @@ -29,7 +29,7 @@ else end end -local unitEventValidator = CreateFrame('Frame') +local unitEventValidator = CreateFrame("Frame") local function IsUnitEventValid(event, unit) -- C_EventUntils.IsEventValid doesn't cover unit events, so we'll have to do this the old fashioned way local isValid = pcall(unitEventValidator.RegisterUnitEvent, unitEventValidator, event, unit) @@ -39,23 +39,23 @@ local function IsUnitEventValid(event, unit) return isValid end -local unitValidator = CreateFrame('Frame') +local unitValidator = CreateFrame("Frame") local function IsUnitValid(unit) - if unitValidator:RegisterUnitEvent('UNIT_HEALTH', unit) then - local _, registeredUnit = unitValidator:IsEventRegistered('UNIT_HEALTH') - unitValidator:UnregisterEvent('UNIT_HEALTH') + if unitValidator:RegisterUnitEvent("UNIT_HEALTH", unit) then + local _, registeredUnit = unitValidator:IsEventRegistered("UNIT_HEALTH") + unitValidator:UnregisterEvent("UNIT_HEALTH") return not not registeredUnit -- it will be nil if the registered unit is invalid end end local eventMixin = {} ---[[ namespace.eventMixin:RegisterEvent(_event_, _callback_) +--[[ namespace.eventMixin:RegisterEvent(_event_, _callback_) ![](https://img.shields.io/badge/function-blue) Registers a [frame `event`](https://warcraft.wiki.gg/wiki/Events) with the `callback` function. If the callback returns positive it will be unregistered. --]] function eventMixin:RegisterEvent(event, callback) - assert(IsEventValid(event), 'arg1 must be an event') - assert(type(callback) == 'function', 'arg2 must be a function') + assert(IsEventValid(event), "arg1 must be an event") + assert(type(callback) == "function", "arg2 must be a function") if not callbacks[event] then callbacks[event] = {} @@ -71,12 +71,12 @@ function eventMixin:RegisterEvent(event, callback) end end ---[[ namespace.eventMixin:UnregisterEvent(_event_, _callback_) +--[[ namespace.eventMixin:UnregisterEvent(_event_, _callback_) ![](https://img.shields.io/badge/function-blue) Unregisters a [frame `event`](https://warcraft.wiki.gg/wiki/Events) from the `callback` function. --]] function eventMixin:UnregisterEvent(event, callback) - assert(IsEventValid(event), 'arg1 must be an event') - assert(type(callback) == 'function', 'arg2 must be a function') + assert(IsEventValid(event), "arg1 must be an event") + assert(type(callback) == "function", "arg2 must be a function") if callbacks[event] then for index, data in next, callbacks[event] do @@ -92,11 +92,11 @@ function eventMixin:UnregisterEvent(event, callback) end end ---[[ namespace.eventMixin:UnregisterAllEvents(_callback_) +--[[ namespace.eventMixin:UnregisterAllEvents(_callback_) ![](https://img.shields.io/badge/function-blue) Unregisters all [frame events](https://warcraft.wiki.gg/wiki/Events) from the `callback` function. --]] function eventMixin:UnregisterAllEvents(callback) - assert(type(callback) == 'function', 'arg1 must be a function') + assert(type(callback) == "function", "arg1 must be a function") for event, cbs in next, callbacks do for _, data in next, cbs do @@ -107,12 +107,12 @@ function eventMixin:UnregisterAllEvents(callback) end end ---[[ namespace.eventMixin:IsEventRegistered(_event_, _callback_) +--[[ namespace.eventMixin:IsEventRegistered(_event_, _callback_) ![](https://img.shields.io/badge/function-blue) Checks if the [frame `event`](https://warcraft.wiki.gg/wiki/Events) is registered with the `callback` function. --]] function eventMixin:IsEventRegistered(event, callback) - assert(IsEventValid(event), 'arg1 must be an event') - assert(type(callback) == 'function', 'arg2 must be a function') + assert(IsEventValid(event), "arg1 must be an event") + assert(type(callback) == "function", "arg2 must be a function") if callbacks[event] then for _, data in next, callbacks[event] do @@ -123,7 +123,7 @@ function eventMixin:IsEventRegistered(event, callback) end end ---[[ namespace.eventMixin:TriggerEvent(_event_[, _..._]) +--[[ namespace.eventMixin:TriggerEvent(_event_[, _..._]) ![](https://img.shields.io/badge/function-blue) Manually trigger the `event` (with optional arguments) on all registered callbacks. If the callback returns positive it will be unregistered. --]] @@ -143,7 +143,7 @@ function eventMixin:TriggerEvent(event, ...) end end -eventHandler:SetScript('OnEvent', function(_, event, ...) +eventHandler:SetScript("OnEvent", function(_, event, ...) eventMixin:TriggerEvent(event, ...) end) @@ -151,8 +151,8 @@ end) local unitEventHandlers = {} local function getUnitEventHandler(unit) if not unitEventHandlers[unit] then - local unitEventHandler = CreateFrame('Frame') - unitEventHandler:SetScript('OnEvent', function(_, event, ...) + local unitEventHandler = CreateFrame("Frame") + unitEventHandler:SetScript("OnEvent", function(_, event, ...) eventMixin:TriggerUnitEvent(event, unit, ...) end) unitEventHandlers[unit] = unitEventHandler @@ -161,18 +161,18 @@ local function getUnitEventHandler(unit) end local unitEventCallbacks = {} ---[[ namespace.eventMixin:RegisterUnitEvent(_event_, _unit_[, _unitN,..._], _callback_) +--[[ namespace.eventMixin:RegisterUnitEvent(_event_, _unit_[, _unitN,..._], _callback_) ![](https://img.shields.io/badge/function-blue) Registers a [`unit`](https://warcraft.wiki.gg/wiki/UnitId)-specific [frame `event`](https://warcraft.wiki.gg/wiki/Events) with the `callback` function. If the callback returns positive it will be unregistered for that unit. --]] function eventMixin:RegisterUnitEvent(event, ...) - assert(IsEventValid(event), 'arg1 must be an event') - local callback = select(select('#', ...), ...) - assert(type(callback) == 'function', 'last argument must be a function') + assert(IsEventValid(event), "arg1 must be an event") + local callback = select(select("#", ...), ...) + assert(type(callback) == "function", "last argument must be a function") - for i = 1, select('#', ...) - 1 do + for i = 1, select("#", ...) - 1 do local unit = select(i, ...) - assert(IsUnitValid(unit), 'arg' .. (i + 1) .. ' must be a valid unit') + assert(IsUnitValid(unit), "arg" .. (i + 1) .. " must be a valid unit") assert(IsUnitEventValid(event, unit), 'event "' .. event .. '" is not valid for the given unit') if not unitEventCallbacks[unit] then @@ -192,23 +192,23 @@ function eventMixin:RegisterUnitEvent(event, ...) if not isRegistered then unitEventHandler:RegisterUnitEvent(event, unit) elseif registeredUnit ~= unit then - error('unit event somehow registered with the wrong unit') + error("unit event somehow registered with the wrong unit") end end end ---[[ namespace.eventMixin:UnregisterUnitEvent(_event_, _unit_[, _unitN,..._], _callback_) +--[[ namespace.eventMixin:UnregisterUnitEvent(_event_, _unit_[, _unitN,..._], _callback_) ![](https://img.shields.io/badge/function-blue) Unregisters a [`unit`](https://warcraft.wiki.gg/wiki/UnitId)-specific [frame `event`](https://warcraft.wiki.gg/wiki/Events) from the `callback` function. --]] function eventMixin:UnregisterUnitEvent(event, ...) - assert(IsEventValid(event), 'arg1 must be an event') - local callback = select(select('#', ...), ...) - assert(type(callback) == 'function', 'last argument must be a function') + assert(IsEventValid(event), "arg1 must be an event") + local callback = select(select("#", ...), ...) + assert(type(callback) == "function", "last argument must be a function") - for i = 1, select('#', ...) - 1 do + for i = 1, select("#", ...) - 1 do local unit = select(i, ...) - assert(IsUnitValid(unit), 'arg' .. (i + 1) .. ' must be a valid unit') - assert(IsUnitEventValid(event, unit), 'event is not valid for the given unit') + assert(IsUnitValid(unit), "arg" .. (i + 1) .. " must be a valid unit") + assert(IsUnitEventValid(event, unit), "event is not valid for the given unit") if unitEventCallbacks[unit] and unitEventCallbacks[unit][event] then for index, data in next, unitEventCallbacks[unit][event] do @@ -225,18 +225,18 @@ function eventMixin:UnregisterUnitEvent(event, ...) end end ---[[ namespace.eventMixin:IsUnitEventRegistered(_event_, _unit_[, _unitN,..._], _callback_) +--[[ namespace.eventMixin:IsUnitEventRegistered(_event_, _unit_[, _unitN,..._], _callback_) ![](https://img.shields.io/badge/function-blue) Checks if the [`unit`](https://warcraft.wiki.gg/wiki/UnitId)-specific [frame `event`](https://warcraft.wiki.gg/wiki/Events) is registered with the `callback` function. --]] function eventMixin:IsUnitEventRegistered(event, ...) - assert(IsEventValid(event), 'arg1 must be an event') - local callback = select(select('#', ...), ...) - assert(type(callback) == 'function', 'last argument must be a function') + assert(IsEventValid(event), "arg1 must be an event") + local callback = select(select("#", ...), ...) + assert(type(callback) == "function", "last argument must be a function") - for i = 1, select('#', ...) - 1 do + for i = 1, select("#", ...) - 1 do local unit = select(i, ...) - assert(IsUnitValid(unit), 'arg' .. (i + 1) .. ' must be a valid unit') - assert(IsUnitEventValid(event, unit), 'event is not valid for the given unit') + assert(IsUnitValid(unit), "arg" .. (i + 1) .. " must be a valid unit") + assert(IsUnitEventValid(event, unit), "event is not valid for the given unit") if unitEventCallbacks[unit] and unitEventCallbacks[unit][event] then for _, data in next, unitEventCallbacks[unit][event] do @@ -248,7 +248,7 @@ function eventMixin:IsUnitEventRegistered(event, ...) end end ---[[ namespace.eventMixin:TriggerEvent(_event_, _unit_[, _unitN,..._][, _..._]) +--[[ namespace.eventMixin:TriggerEvent(_event_, _unit_[, _unitN,..._][, _..._]) ![](https://img.shields.io/badge/function-blue) Manually trigger the [`unit`](https://warcraft.wiki.gg/wiki/UnitId)-specific `event` (with optional arguments) on all registered callbacks. If the callback returns positive it will be unregistered. --]] @@ -268,13 +268,13 @@ end -- special handling for combat events local combatEventCallbacks = {} ---[[ namespace.eventMixin:RegisterCombatEvent(_subEvent_, _callback_) +--[[ namespace.eventMixin:RegisterCombatEvent(_subEvent_, _callback_) ![](https://img.shields.io/badge/function-blue) Registers a [combat `subEvent`](https://warcraft.wiki.gg/wiki/COMBAT_LOG_EVENT) with the `callback` function. If the callback returns positive it will be unregistered. --]] function eventMixin:RegisterCombatEvent(event, callback) - assert(type(event) == 'string', 'arg1 must be a string') - assert(type(callback) == 'function', 'arg2 must be a function') + assert(type(event) == "string", "arg1 must be a string") + assert(type(callback) == "function", "arg2 must be a function") if not combatEventCallbacks[event] then combatEventCallbacks[event] = {} @@ -285,17 +285,17 @@ function eventMixin:RegisterCombatEvent(event, callback) owner = self, }) - if not self:IsEventRegistered('COMBAT_LOG_EVENT_UNFILTERED', self.TriggerCombatEvent) then - self:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED', self.TriggerCombatEvent) + if not self:IsEventRegistered("COMBAT_LOG_EVENT_UNFILTERED", self.TriggerCombatEvent) then + self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED", self.TriggerCombatEvent) end end ---[[ namespace.eventMixin:UnregisterCombatEvent(_subEvent_, _callback_) +--[[ namespace.eventMixin:UnregisterCombatEvent(_subEvent_, _callback_) ![](https://img.shields.io/badge/function-blue) Unregisters a [combat `subEvent`](https://warcraft.wiki.gg/wiki/COMBAT_LOG_EVENT) from the `callback` function. --]] function eventMixin:UnregisterCombatEvent(event, callback) - assert(type(event) == 'string', 'arg1 must be a string') - assert(type(callback) == 'function', 'arg2 must be a function') + assert(type(event) == "string", "arg1 must be a string") + assert(type(callback) == "function", "arg2 must be a function") if combatEventCallbacks[event] then for index, data in next, combatEventCallbacks[event] do @@ -307,7 +307,7 @@ function eventMixin:UnregisterCombatEvent(event, callback) end end ---[[ namespace.eventMixin:TriggerCombatEvent(_subEvent_) +--[[ namespace.eventMixin:TriggerCombatEvent(_subEvent_) ![](https://img.shields.io/badge/function-blue) Manually trigger the [combat `subEvent`](https://warcraft.wiki.gg/wiki/COMBAT_LOG_EVENT) on all registered callbacks. If the callback returns positive it will be unregistered. @@ -338,9 +338,9 @@ addon.eventMixin = eventMixin -- anonymous event registration addon = setmetatable(addon, { __newindex = function(t, key, value) - if key == 'OnLoad' then - --[[ namespace:OnLoad() - Shorthand for the [`ADDON_LOADED`](https://warcraft.wiki.gg/wiki/ADDON_LOADED) for the addon. + if key == "OnLoad" then + --[[ namespace:OnLoad() ![](https://img.shields.io/badge/function-blue) + Shorthand for the [`ADDON_LOADED`](https://warcraft.wiki.gg/wiki/ADDON_LOADED) event for the addon. Usage: ```lua @@ -349,7 +349,7 @@ addon = setmetatable(addon, { end ``` --]] - addon:RegisterEvent('ADDON_LOADED', function(self, name) + addon:RegisterEvent("ADDON_LOADED", function(self, name) if name == addonName then local successful, ret = pcall(value, self) if not successful then @@ -358,9 +358,9 @@ addon = setmetatable(addon, { return true -- unregister event end end) - elseif key == 'OnLogin' then - --[[ namespace:OnLogin() - Shorthand for the [`PLAYER_LOGIN`](https://warcraft.wiki.gg/wiki/PLAYER_LOGIN). + elseif key == "OnLogin" then + --[[ namespace:OnLogin() ![](https://img.shields.io/badge/function-blue) + Shorthand for the [`PLAYER_LOGIN`](https://warcraft.wiki.gg/wiki/PLAYER_LOGIN) event. Usage: ```lua @@ -369,7 +369,7 @@ addon = setmetatable(addon, { end ``` --]] - addon:RegisterEvent('PLAYER_LOGIN', function(self) + addon:RegisterEvent("PLAYER_LOGIN", function(self) local successful, ret = pcall(value, self) if not successful then error(ret) @@ -377,7 +377,7 @@ addon = setmetatable(addon, { return true -- unregister event end) elseif IsEventValid(key) then - --[[ namespace:_event_ + --[[ namespace:_event_ ![](https://img.shields.io/badge/function-blue) Registers a to an anonymous function. Usage: @@ -385,6 +385,10 @@ addon = setmetatable(addon, { function namespace:BAG_UPDATE(bagID) -- do something end + -- or + namespace.BAG_UPDATE = function(self, bagID) + -- do something + end ``` --]] eventMixin.RegisterEvent(t, key, value) @@ -395,7 +399,7 @@ addon = setmetatable(addon, { end, __index = function(t, key) if IsEventValid(key) then - --[[ namespace:_event_([_..._]) + --[[ namespace:_event_([_..._]) ![](https://img.shields.io/badge/function-blue) Manually trigger all registered anonymous `event` callbacks, with optional arguments. Usage: diff --git a/LevelUpFX/Libs/Dashi/modules/input.lua b/LevelUpFX/Libs/Dashi/modules/input.lua index b421f30..9cd8cd1 100644 --- a/LevelUpFX/Libs/Dashi/modules/input.lua +++ b/LevelUpFX/Libs/Dashi/modules/input.lua @@ -1,6 +1,6 @@ local addonName, addon = ... ---[[ namespace:RegisterSlash(_command_[, _commandN,..._], _callback_) +--[[ namespace:RegisterSlash(_command_[, _commandN,..._], _callback_) ![](https://img.shields.io/badge/function-blue) Registers chat slash `command`(s) with a `callback` function. Usage: @@ -11,24 +11,24 @@ end) ``` --]] function addon:RegisterSlash(...) - local name = addonName .. 'Slash' .. math.random() + local name = addonName .. "Slash" .. math.random() local failed - local numArgs = select('#', ...) + local numArgs = select("#", ...) local callback = select(numArgs, ...) - if type(callback) ~= 'function' or numArgs < 2 then + if type(callback) ~= "function" or numArgs < 2 then failed = true else for index = 1, numArgs - 1 do local slash = select(index, ...) - if type(slash) ~= 'string' then + if type(slash) ~= "string" then failed = true break - elseif not slash:match('^/%a+$') then + elseif not slash:match("^/%a+$") then failed = true break else - _G['SLASH_' .. name .. index] = slash + _G["SLASH_" .. name .. index] = slash end end end diff --git a/LevelUpFX/Libs/Dashi/modules/library.lua b/LevelUpFX/Libs/Dashi/modules/library.lua index 0377bb8..29e7b2c 100644 --- a/LevelUpFX/Libs/Dashi/modules/library.lua +++ b/LevelUpFX/Libs/Dashi/modules/library.lua @@ -1,7 +1,7 @@ local _, addon = ... ---[[ namespace:tsize(_table_) -Returns the number of entries in the `table`. +--[[ namespace:tsize(_tbl_) ![](https://img.shields.io/badge/function-blue) +Returns the number of entries in the table `tbl`. Works for associative tables as opposed to `#table`. --]] function addon:tsize(tbl) @@ -15,9 +15,69 @@ function addon:tsize(tbl) return size end ---[[ namespace:startswith(_str_, _contents_) +--[[ namespace:startswith(_str_, _contents_) ![](https://img.shields.io/badge/function-blue) Checks if the first string starts with the 2nd string. --]] function addon:startswith(str, contents) return str:sub(1, contents:len()) == contents end + +--[[ namespace:T(_tbl_[, _mixin_, ...]) ![](https://img.shields.io/badge/function-blue) +Returns the table _`tbl`_ with meta methods. + +Included are all meta methods from the `table` library, as well as a few extra handy methods: + +- `tbl:size()` returns the length of the table irregardless if it's indexed or associative +- `tbl:contains(value)` returns `true` if the table contains the given `value`, otherwise `false` +- `tbl:merge(t)` merges (and returns) the table with the supplied table `t` + - can also be used by using an addition arithmetic metamethod + +It's also possible to add extra meta methods by supplying mixins through the variable argument. + +Example usage: + +```lua +local t = namespace:T{'one', 'two'} +t:insert('three') +t:size() --> 3 +t:contains('four') --> false +t + {'five', 'six'} --> {'one', 'two', 'three', 'five', 'six'} +``` +--]] +do + local tableMixin = {} + function tableMixin:size() + local n = 0 + for _ in next, self do + n = n + 1 + end + return n + end + + function tableMixin:merge(t) + for k, v in next, t do + if type(self[k] or false) == "table" then + tableMixin.merge(self[k], t[k]) + else + self[k] = v + end + end + return self + end + + function tableMixin:contains(value) + for _, v in next, self do + if value == v then + return true + end + end + return false + end + + function addon:T(tbl, ...) + return setmetatable(tbl or {}, { + __index = Mixin(table, tableMixin, ...), + __add = tableMixin.merge, + }) + end +end diff --git a/LevelUpFX/Libs/Dashi/modules/load.lua b/LevelUpFX/Libs/Dashi/modules/load.lua index 5331306..f070f49 100644 --- a/LevelUpFX/Libs/Dashi/modules/load.lua +++ b/LevelUpFX/Libs/Dashi/modules/load.lua @@ -1,15 +1,14 @@ local _, addon = ... ---[[ namespace:IsAddOnEnabled(addonName) +--[[ namespace:IsAddOnEnabled(addonName) ![](https://img.shields.io/badge/function-blue) Checks whether the addon exists and is enabled. --]] function addon:IsAddOnEnabled(name) - return C_AddOns.GetAddOnEnableState(name, UnitName('player')) > 0 + return C_AddOns.GetAddOnEnableState(name, UnitName("player")) > 0 end - local addonCallbacks = {} ---[[ namespace:HookAddOn(_addonName_, _callback_) +--[[ namespace:HookAddOn(_addonName_, _callback_) ![](https://img.shields.io/badge/function-blue) Registers a hook for when an addon with the name `addonName` loads with a `callback` function. --]] function addon:HookAddOn(addonName, callback) @@ -23,7 +22,7 @@ function addon:HookAddOn(addonName, callback) end end -addon:RegisterEvent('ADDON_LOADED', function(self, addonName) +addon:RegisterEvent("ADDON_LOADED", function(self, addonName) for _, info in next, addonCallbacks do if info.addonName == addonName then local successful, err = pcall(info.callback) diff --git a/LevelUpFX/Libs/Dashi/modules/locale.lua b/LevelUpFX/Libs/Dashi/modules/locale.lua index 1a03611..45df164 100644 --- a/LevelUpFX/Libs/Dashi/modules/locale.lua +++ b/LevelUpFX/Libs/Dashi/modules/locale.lua @@ -4,7 +4,19 @@ local _, addon = ... local localizations = {} local locale = GetLocale() ---[[ namespace.L(_locale_)[`string`] +--[[ namespace.L ![](https://img.shields.io/badge/object-teal) +Metatable used specifically for managing localizations, see below for interactions. +--]] +--[[ namespace.L(_locale_) ![](https://img.shields.io/badge/function-blue) +Returns a localization object specific to the given `locale`. + +Usage: +```lua +local L = namespace.L('frFR') +-- see methods below +``` +--]] +--[[ namespace.L(_locale_)[`string`] ![](https://img.shields.io/badge/object-teal) Sets a localization `string` for the given `locale`. Usage: @@ -13,7 +25,7 @@ local L = namespace.L('deDE') L['New string'] = 'Neue saite' ``` --]] ---[[ namespace.L[`string`] +--[[ namespace.L[`string`] ![](https://img.shields.io/badge/object-teal) Reads a localized `string` for the active locale. If a localized string for the active locale is not available the `string` will be read back. diff --git a/LevelUpFX/Libs/Dashi/modules/misc.lua b/LevelUpFX/Libs/Dashi/modules/misc.lua index 4d820b9..e064798 100644 --- a/LevelUpFX/Libs/Dashi/modules/misc.lua +++ b/LevelUpFX/Libs/Dashi/modules/misc.lua @@ -8,26 +8,26 @@ local _, namespace = ... ``` --]] ---[[ namespace:ArgCheck(arg, argIndex, type[, type...]) -Checks if the argument `arg` at position `argIndex` is of type(s). +--[[ namespace:ArgCheck(arg, argIndex, type[, type...]) ![](https://img.shields.io/badge/function-blue) +Checks if the argument `arg` at position `argIndex` is of `type`(s). --]] function addon:ArgCheck(arg, argIndex, ...) - assert(type(argIndex) == 'number', 'Bad argument #2 to \'ArgCheck\' (number expected, got ' .. type(argIndex) .. ')') + assert(type(argIndex) == "number", "Bad argument #2 to 'ArgCheck' (number expected, got " .. type(argIndex) .. ")") - for index = 1, select('#', ...) do + for index = 1, select("#", ...) do if type(arg) == select(index, ...) then return end end - local types = string.join(', ', ...) - local name = debugstack(2, 2, 0):match(': in function [`<](.-)[\'>]') - error(string.format('Bad argument #%d to \'%s\' (%s expected, got %s)', argIndex, name, types, type(arg)), 3) + local types = string.join(", ", ...) + local name = debugstack(2, 2, 0):match(": in function [`<](.-)['>]") + error(string.format("Bad argument #%d to '%s' (%s expected, got %s)", argIndex, name, types, type(arg)), 3) end do -- UnitType-0-ServerID-InstanceID-ZoneUID-ID-SpawnUID - local GUID_PATTERN = '(%w+)%-0%-(%d+)%-(%d+)%-(%d+)%-(%d+)%-(.+)' + local GUID_PATTERN = "(%w+)%-0%-(%d+)%-(%d+)%-(%d+)%-(%d+)%-(.+)" --[[ namespace:ExtractFieldsFromUnitGUID(_guid_) Returns the individual fields from the given [`guid`](https://warcraft.wiki.gg/wiki/GUID), typecast to their correct types. --]] @@ -41,7 +41,7 @@ do end end ---[[ namespace:GetUnitID(_unit_) +--[[ namespace:GetUnitID(_unit_) ![](https://img.shields.io/badge/function-blue) Returns the integer `id` of the given [`unit`](https://warcraft.wiki.gg/wiki/UnitId). --]] function addon:GetUnitID(unit) @@ -51,7 +51,7 @@ function addon:GetUnitID(unit) end end ---[[ namespace:GetNPCName(_npcID_) +--[[ namespace:GetNPCName(_npcID_) ![](https://img.shields.io/badge/function-blue) Returns the name for the NPC by the given `npcID`. Warning: this depends on the cache, and might not yield results the first time. @@ -59,13 +59,13 @@ Warning: this depends on the cache, and might not yield results the first time. do local creatureNames = setmetatable({}, { __index = function(self, npcID) - local data = C_TooltipInfo.GetHyperlink('unit:Creature-0-0-0-0-' .. npcID .. '-0') + local data = C_TooltipInfo.GetHyperlink("unit:Creature-0-0-0-0-" .. npcID .. "-0") local name = data and data.lines and data.lines[1] and data.lines[1].leftText if name then rawset(self, npcID, name) return name end - end + end, }) function addon:GetNPCName(npcID) @@ -74,8 +74,8 @@ do end do - local ITEM_LINK_FORMAT = '|Hitem:%d|h' - --[[ namespace:GetItemLinkFromID(_itemID_) + local ITEM_LINK_FORMAT = "|Hitem:%d|h" + --[[ namespace:GetItemLinkFromID(_itemID_) ![](https://img.shields.io/badge/function-blue) Generates an [item link](https://warcraft.wiki.gg/wiki/ItemLink) from an `itemID`. This is a crude generation and won't have valid data for complex items. --]] @@ -84,19 +84,19 @@ do end end ---[[ namespace:GetPlayerMapID() +--[[ namespace:GetPlayerMapID() ![](https://img.shields.io/badge/function-blue) Returns the ID of the current map the zone the player is located in. --]] function addon:GetPlayerMapID() -- TODO: maybe use HBD data if it's available - return C_Map.GetBestMapForUnit('player') or -1 + return C_Map.GetBestMapForUnit("player") or -1 end ---[[ namespace:GetPlayerPosition(_mapID_) +--[[ namespace:GetPlayerPosition(_mapID_) ![](https://img.shields.io/badge/function-blue) Returns the `x` and `y` coordinates for the player in the given `mapID` (if they are valid). --]] function addon:GetPlayerPosition(mapID) - local pos = C_Map.GetPlayerMapPosition(mapID, 'player') + local pos = C_Map.GetPlayerMapPosition(mapID, "player") if pos then return pos:GetXY() end @@ -105,7 +105,7 @@ end do local function auraSlotsWrapper(unit, spellID, token, ...) local slot, data - for index = 1, select('#', ...) do + for index = 1, select("#", ...) do slot = select(index, ...) data = C_UnitAuras.GetAuraDataBySlot(unit, slot) if spellID == data.spellId and data.sourceUnit then @@ -116,12 +116,9 @@ do return token end - --[[ namespace:GetUnitAura(_unit_, _spellID_, _filter_) - Returns the aura by `spellID` on the [`unit`](https://warcraft.wiki.gg/wiki/UnitId), if it exists. - - * [`unitID`](https://warcraft.wiki.gg/wiki/UnitId) - * `spellID` - spell ID to check for - * `filter` - aura filter, see [UnitAura](https://warcraft.wiki.gg/wiki/API_C_UnitAuras.GetAuraDataByIndex#Filters) + --[[ namespace:GetUnitAura(_unitID_, _spellID_[, _filter_]) ![](https://img.shields.io/badge/function-blue) + Returns the aura by `spellID` on the [`unitID`](https://warcraft.wiki.gg/wiki/UnitId), if it exists. + See [UnitAura](https://warcraft.wiki.gg/wiki/API_C_UnitAuras.GetAuraDataByIndex#Filters) for the `filter` arg. --]] function addon:GetUnitAura(unit, spellID, filter) local token, data @@ -133,19 +130,19 @@ do end end ---[[ namespace:CreateColor(r, g, b[, a]) +--[[ namespace:CreateColor(r, g, b[, a]) ![](https://img.shields.io/badge/function-blue) Wrapper for CreateColor that can handle >1-255 range as well. Alpha (`a`) will always be in the 0-1 range. --]] ---[[ namespace:CreateColor(hex) +--[[ namespace:CreateColor(hex) ![](https://img.shields.io/badge/function-blue) Wrapper for CreateColor that can handle hex colors (both `RRGGBB` and `AARRGGBB`). --]] function addon:CreateColor(r, g, b, a) - if type(r) == 'table' then + if type(r) == "table" then return addon:CreateColor(r.r, r.g, r.b, r.a) - elseif type(r) == 'string' then + elseif type(r) == "string" then -- load from hex - local hex = r:gsub('#', '') + local hex = r:gsub("#", "") if #hex == 8 then -- prefixed with alpha a = tonumber(hex:sub(1, 2), 16) / 255 @@ -175,7 +172,7 @@ do local timeFormatter = CreateFromMixins(SecondsFormatterMixin) timeFormatter:Init(1, SecondsFormatter.Abbreviation.OneLetter) timeFormatter:SetStripIntervalWhitespace(true) - --[[ namespace:FormatTime(_timeInSeconds_) + --[[ namespace:FormatTime(_timeInSeconds_) ![](https://img.shields.io/badge/function-blue) Formats the given `timeInSeconds` to a readable, but abbreviated format. --]] function addon:FormatTime(timeInSeconds) diff --git a/LevelUpFX/Libs/Dashi/modules/object.lua b/LevelUpFX/Libs/Dashi/modules/object.lua index 041f2c3..562b346 100644 --- a/LevelUpFX/Libs/Dashi/modules/object.lua +++ b/LevelUpFX/Libs/Dashi/modules/object.lua @@ -1,21 +1,28 @@ local _, addon = ... -- easy frame "removal" -local hidden = CreateFrame('Frame') +local hidden = CreateFrame("Frame") hidden:Hide() ---[[ namespace:Hide(_object_[, _child_,...]) +--[[ namespace:Hide(_object_[, _child_,...]) ![](https://img.shields.io/badge/function-blue) Forcefully hide an `object`, or its `child`. It will recurse down to the last child if provided. + +Usage: +```lua +namespace:Hide('ChatFrame2') +namespace:Hide('MinimapCluster', 'InstanceDifficulty') +namespace:Hide(someFrame, 'ResetButton') +``` --]] function addon:Hide(object, ...) - if type(object) == 'string' then + if type(object) == "string" then object = _G[object] end if ... then -- iterate through arguments, they're children referenced by key - for index = 1, select('#', ...) do + for index = 1, select("#", ...) do object = object[select(index, ...)] end end diff --git a/LevelUpFX/Libs/Dashi/modules/output.lua b/LevelUpFX/Libs/Dashi/modules/output.lua index 9d0477f..ad89c46 100644 --- a/LevelUpFX/Libs/Dashi/modules/output.lua +++ b/LevelUpFX/Libs/Dashi/modules/output.lua @@ -1,37 +1,37 @@ local addonName, addon = ... ---[[ namespace:Print(_..._) +--[[ namespace:Print(_..._) ![](https://img.shields.io/badge/function-blue) Prints out a message in the chat frame, prefixed with the addon name in color. --]] function addon:Print(...) -- can't use string join, it fails on nil values - local msg = '' - for index = 1, select('#', ...) do + local msg = "" + for index = 1, select("#", ...) do local arg = select(index, ...) - msg = msg .. tostring(arg) .. ' ' + msg = msg .. tostring(arg) .. " " end - DEFAULT_CHAT_FRAME:AddMessage('|cff33ff99' .. addonName .. '|r: ' .. msg:trim()) + DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99" .. addonName .. "|r: " .. msg:trim()) end ---[[ namespace:Printf(_fmt_, _..._) +--[[ namespace:Printf(_fmt_, _..._) ![](https://img.shields.io/badge/function-blue) Wrapper for `namespace:Print(...)` and `string.format`. --]] function addon:Printf(fmt, ...) self:Print(fmt:format(...)) end ---[[ namespace:Dump(_object_[, _startKey_]) +--[[ namespace:Dump(_object_[, _startKey_]) ![](https://img.shields.io/badge/function-blue) Wrapper for `DevTools_Dump`. --]] function addon:Dump(value, startKey) DevTools_Dump(value, startKey) end ---[[ namespace:DumpUI(_object_) +--[[ namespace:DumpUI(_object_) ![](https://img.shields.io/badge/function-blue) Similar to `namespace:Dump(object)`; a wrapper for the graphical version. --]] function addon:DumpUI(value) - UIParentLoadAddOn('Blizzard_DebugTools') + UIParentLoadAddOn("Blizzard_DebugTools") DisplayTableInspectorWindow(value) end diff --git a/LevelUpFX/Libs/Dashi/modules/settings.lua b/LevelUpFX/Libs/Dashi/modules/settings.lua index 0b32b1e..fb76c07 100644 --- a/LevelUpFX/Libs/Dashi/modules/settings.lua +++ b/LevelUpFX/Libs/Dashi/modules/settings.lua @@ -14,48 +14,48 @@ do function canvasMixin:SetDefaultsHandler(callback) local button = self:GetParent().Header.DefaultsButton button:Show() - button:SetScript('OnClick', callback) + button:SetScript("OnClick", callback) end function createCanvas(name) - local frame = CreateFrame('Frame') + local frame = CreateFrame("Frame") -- replicate header from SettingsListTemplate - local header = CreateFrame('Frame', nil, frame) - header:SetPoint('TOPLEFT') - header:SetPoint('TOPRIGHT') + local header = CreateFrame("Frame", nil, frame) + header:SetPoint("TOPLEFT") + header:SetPoint("TOPRIGHT") header:SetHeight(50) frame.Header = header - local title = header:CreateFontString(nil, 'ARTWORK', 'GameFontHighlightHuge') - title:SetPoint('TOPLEFT', 7, -22) - title:SetJustifyH('LEFT') - title:SetText(string.format('%s - %s', addonName, name)) + local title = header:CreateFontString(nil, "ARTWORK", "GameFontHighlightHuge") + title:SetPoint("TOPLEFT", 7, -22) + title:SetJustifyH("LEFT") + title:SetText(string.format("%s - %s", addonName, name)) header.Title = title - local defaults = CreateFrame('Button', nil, header, 'UIPanelButtonTemplate') - defaults:SetPoint('TOPRIGHT', -36, -16) + local defaults = CreateFrame("Button", nil, header, "UIPanelButtonTemplate") + defaults:SetPoint("TOPRIGHT", -36, -16) defaults:SetSize(96, 22) defaults:SetText(SETTINGS_DEFAULTS) defaults:Hide() header.DefaultsButton = defaults - local divider = header:CreateTexture(nil, 'ARTWORK') - divider:SetPoint('TOP', 0, -50) - divider:SetAtlas('Options_HorizontalDivider', true) + local divider = header:CreateTexture(nil, "ARTWORK") + divider:SetPoint("TOP", 0, -50) + divider:SetAtlas("Options_HorizontalDivider", true) -- exposed container the addon can use - local canvas = Mixin(CreateFrame('Frame', nil, frame), canvasMixin) - canvas:SetPoint('BOTTOMLEFT', 0, 5) - canvas:SetPoint('BOTTOMRIGHT', -12, 5) - canvas:SetPoint('TOP', 0, -56) + local canvas = Mixin(CreateFrame("Frame", nil, frame), canvasMixin) + canvas:SetPoint("BOTTOMLEFT", 0, 5) + canvas:SetPoint("BOTTOMRIGHT", -12, 5) + canvas:SetPoint("TOP", 0, -56) return frame, canvas end end -local createColorPicker -- I wish Settings.CreateColorPicker was a thing -do +local createColorPicker +do -- I wish Settings.CreateColorPicker was a thing local colorPickerMixin = {} function colorPickerMixin:OnSettingValueChanged(setting, value) local r, g, b, a = addon:CreateColor(value):GetRGBA() @@ -102,10 +102,10 @@ do self:SetSize(280, 26) -- templates have a size -- creating widgets would be equal to :OnLoad() - self.Swatch = CreateFrame('Button', nil, self, 'ColorSwatchTemplate') + self.Swatch = CreateFrame("Button", nil, self, "ColorSwatchTemplate") self.Swatch:SetSize(30, 30) - self.Swatch:SetPoint('LEFT', self, 'CENTER', -80, 0) - self.Swatch:SetScript('OnClick', onClick) + self.Swatch:SetPoint("LEFT", self, "CENTER", -80, 0) + self.Swatch:SetScript("OnClick", onClick) -- setting up state would be equal to :Init() local setting = initializer:GetSetting() @@ -120,7 +120,7 @@ do g = g, b = b, opacity = a, - hasOpacity = #value == 8 + hasOpacity = #value == 8, } self.Swatch:SetColorRGB(r, g, b) @@ -132,10 +132,11 @@ do function createColorPicker(category, setting, options, tooltip) local data = Settings.CreateSettingInitializerData(setting, options, tooltip) - local init = Settings.CreateElementInitializer('SettingsListElementTemplate', data) + local init = Settings.CreateElementInitializer("SettingsListElementTemplate", data) init.InitFrame = initFrame init:AddSearchTags(setting:GetName()) SettingsPanel:GetLayout(category):AddInitializer(init) + return init end end @@ -144,31 +145,33 @@ local function formatCustom(fmt, value) end local function registerSetting(category, savedvariable, info) - addon:ArgCheck(info.key, 3, 'string') - addon:ArgCheck(info.title, 3, 'string') - addon:ArgCheck(info.type, 3, 'string') + addon:ArgCheck(info.key, 3, "string") + addon:ArgCheck(info.title, 3, "string") + addon:ArgCheck(info.type, 3, "string") + addon:ArgCheck(info.requires, 3, "string", "nil") assert(info.default ~= nil, "default must be set") - local uniqueKey = savedvariable .. '_' .. info.key + local uniqueKey = savedvariable .. "_" .. info.key local setting = Settings.RegisterAddOnSetting(category, uniqueKey, info.key, _G[savedvariable], type(info.default), info.title, info.default) - if info.type == 'toggle' then - Settings.CreateCheckbox(category, setting, info.tooltip) - elseif info.type == 'slider' then - addon:ArgCheck(info.minValue, 3, 'number') - addon:ArgCheck(info.maxValue, 3, 'number') - addon:ArgCheck(info.valueFormat, 3, 'string', 'function') + local initializer + if info.type == "toggle" then + initializer = Settings.CreateCheckbox(category, setting, info.tooltip) + elseif info.type == "slider" then + addon:ArgCheck(info.minValue, 3, "number") + addon:ArgCheck(info.maxValue, 3, "number") + addon:ArgCheck(info.valueFormat, 3, "string", "function") local options = Settings.CreateSliderOptions(info.minValue, info.maxValue, info.valueStep or 1) - if type(info.valueFormat) == 'string' then + if type(info.valueFormat) == "string" then options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right, GenerateClosure(formatCustom, info.valueFormat)) - elseif type(info.valueFormat) == 'function' then + elseif type(info.valueFormat) == "function" then options:SetLabelFormatter(MinimalSliderWithSteppersMixin.Label.Right, info.valueFormat) end - Settings.CreateSlider(category, setting, options, info.tooltip) - elseif info.type == 'menu' then - addon:ArgCheck(info.options, 3, 'table') + initializer = Settings.CreateSlider(category, setting, options, info.tooltip) + elseif info.type == "menu" then + addon:ArgCheck(info.options, 3, "table") local options = function() local container = Settings.CreateControlTextContainer() for _, option in next, info.options do @@ -176,18 +179,19 @@ local function registerSetting(category, savedvariable, info) end return container:GetData() end - Settings.CreateDropdown(category, setting, options, info.tooltip) - elseif info.type == 'color' or info.type == 'colorpicker' then -- TODO: remove in 12.x, compat - createColorPicker(category, setting, nil, info.tooltip) + + initializer = Settings.CreateDropdown(category, setting, options, info.tooltip) + elseif info.type == "color" or info.type == "colorpicker" then -- TODO: remove in 12.x, compat + initializer = createColorPicker(category, setting, nil, info.tooltip) else - error('type is invalid') -- TODO: make this prettier + error("type is invalid") -- TODO: make this prettier return end if info.firstInstall then -- we don't want to add "new" tags to a freshly installed addon - _G[savedvariable][info.key .. '_seen'] = true - elseif not _G[savedvariable][info.key .. '_seen'] then + _G[savedvariable][info.key .. "_seen"] = true + elseif not _G[savedvariable][info.key .. "_seen"] then -- add new tag to the settings panel until it's been observed by the player -- possibly tainty, definitely ugly local version = GetBuildInfo() @@ -198,9 +202,9 @@ local function registerSetting(category, savedvariable, info) table.insert(NewSettings[version], uniqueKey) -- remove once seen - EventRegistry:RegisterCallback('Settings.CategoryChanged', function(_, cat) - if cat == category and not _G[savedvariable][info.key .. '_seen'] then - _G[savedvariable][info.key .. '_seen'] = true + EventRegistry:RegisterCallback("Settings.CategoryChanged", function(_, cat) + if cat == category and not _G[savedvariable][info.key .. "_seen"] then + _G[savedvariable][info.key .. "_seen"] = true local settingIndex for index, key in next, NewSettings[version] do @@ -226,10 +230,16 @@ local function registerSetting(category, savedvariable, info) -- trigger load callback addon:TriggerOptionCallback(info.key, setting:GetValue()) + + return initializer +end + +local function isSettingEnabled(parentInitializer) + return not not parentInitializer:GetSetting():GetValue() end local function registerSettings(savedvariable, settings) - local categoryName = C_AddOns.GetAddOnMetadata(addonName, 'Title') + local categoryName = C_AddOns.GetAddOnMetadata(addonName, "Title") local category = Settings.RegisterVerticalLayoutCategory(categoryName) Settings.RegisterAddOnCategory(category) @@ -240,12 +250,32 @@ local function registerSettings(savedvariable, settings) firstInstall = true end - for _, setting in next, settings do + local keys = {} + local initializers = {} + local dependents = addon.T({}) + for index, setting in next, settings do if firstInstall then setting.firstInstall = true end - registerSetting(category, savedvariable, setting) + local initializer = registerSetting(category, savedvariable, setting) + keys[setting.key] = index + initializers[setting.key] = initializer + + if setting.requires then + dependents[setting.key] = setting.requires + end + end + + if dependents:size() > 0 then + for key, requires in next, dependents do + -- check if there are bad dependencies + assert(not not keys[requires], string.format("setting '%s' can't depend on invalid setting '%s'", key, requires)) + assert(settings[keys[requires]].type == "toggle", string.format("setting '%s' can't depend on a non-toggle setting", key)) + + -- depend on "parent" setting + initializers[key]:SetParentInitializer(initializers[requires], GenerateClosure(isSettingEnabled, initializers[requires])) + end end -- sub-categories @@ -263,7 +293,7 @@ local function registerSettings(savedvariable, settings) -- delay callback until settings are shown local shown - SettingsPanel:HookScript('OnShow', function() + SettingsPanel:HookScript("OnShow", function() if not shown then info.callback(canvas) shown = true @@ -273,7 +303,7 @@ local function registerSettings(savedvariable, settings) end end ---[[ namespace:RegisterSettings(_savedvariables_, _settings_) +--[[ namespace:RegisterSettings(_savedvariables_, _settings_) ![](https://img.shields.io/badge/function-blue) Registers a set of `settings` with the interface options panel. The values will be stored by the `settings`' objects' `key` in `savedvariables`. @@ -297,8 +327,9 @@ namespace:RegisterSettings('MyAddOnDB', { default = 0.5, minValue = 0.1, maxValue = 1.0, - valueStep = 0.01, + valueStep = 0.01, -- (optional) step value, defaults to 1 valueFormat = formatter, -- callback function or a string for string.format + requires = 'myToggle', -- (optional) dependency on another setting (must be a "toggle") }, { key = 'myMenu', @@ -311,20 +342,22 @@ namespace:RegisterSettings('MyAddOnDB', { {value = key2, label = 'Second option'}, {value = key3, label = 'Third option'}, }, + requires = 'myToggle', -- (optional) dependency on another setting (must be a "toggle") }, { key = 'myColor', type = 'color', title = 'My Color', tooltip = 'Longer description of the color in a tooltip', - default = 'ff00ff', -- either "RRGGBB" or "AARRGGBB" format + default = 'ff00ff', -- either "RRGGBB" or "AARRGGBB" format, the latter enables opacity + requires = 'myToggle', -- (optional) dependency on another setting (must be of type "toggle") } }) ``` --]] function addon:RegisterSettings(savedvariable, settings) - addon:ArgCheck(savedvariable, 1, 'string') - addon:ArgCheck(settings, 2, 'table') + addon:ArgCheck(savedvariable, 1, "string") + addon:ArgCheck(settings, 2, "table") assert(not self.registeredVariables, "can't register settings more than once") self.registeredVariables = savedvariable @@ -338,7 +371,7 @@ function addon:RegisterSettings(savedvariable, settings) registerSettings(savedvariable, settings) else -- don't abuse OnLoad internally - addon:RegisterEvent('ADDON_LOADED', function(_, name) + addon:RegisterEvent("ADDON_LOADED", function(_, name) if name == addonName then registerSettings(savedvariable, settings) return true -- unregister @@ -347,15 +380,15 @@ function addon:RegisterSettings(savedvariable, settings) end end ---[[ namespace:RegisterSubSettings(_name_, _settings_) +--[[ namespace:RegisterSubSettings(_name_, _settings_) ![](https://img.shields.io/badge/function-blue) Registers a set of `settings` as a sub-category. `name` must be unique. The savedvariables will be stored under the main savedvariables in a table entry named after `name`. The `settings` are identical to that of `namespace:RegisterSettings`. --]] function addon:RegisterSubSettings(name, settings) - addon:ArgCheck(name, 1, 'string') - addon:ArgCheck(settings, 2, 'table') + addon:ArgCheck(name, 1, "string") + addon:ArgCheck(settings, 2, "table") assert(not not self.settingsChildren, "can't register sub-settings without root settings") assert(not self.settingsChildren[name], "can't register two sub-settings with the same name") self.settingsChildren[name] = { @@ -364,7 +397,7 @@ function addon:RegisterSubSettings(name, settings) } end ---[[ namespace:RegisterSubSettingsCanvas(_name_, _callback_) +--[[ namespace:RegisterSubSettingsCanvas(_name_, _callback_) ![](https://img.shields.io/badge/function-blue) Registers a canvas sub-category. This does not handle savedvariables. `name` must be unique, and `callback` is called with a canvas `frame` as payload. @@ -373,8 +406,8 @@ Canvas frame has a custom method `SetDefaultsHandler` which takes a callback as This callback is triggered when the "Defaults" button is clicked. --]] function addon:RegisterSubSettingsCanvas(name, callback) - addon:ArgCheck(name, 1, 'string') - addon:ArgCheck(callback, 2, 'function') + addon:ArgCheck(name, 1, "string") + addon:ArgCheck(callback, 2, "function") assert(not not self.settingsChildren, "can't register sub-settings without root settings") assert(not self.settingsChildren[name], "can't register two sub-settings with the same name") self.settingsChildren[name] = { @@ -383,19 +416,19 @@ function addon:RegisterSubSettingsCanvas(name, callback) } end ---[[ namespace:RegisterSettingsSlash(_..._) +--[[ namespace:RegisterSettingsSlash(_..._) ![](https://img.shields.io/badge/function-blue) Wrapper for `namespace:RegisterSlash(...)`, except the callback is provided and will open the settings panel for this addon. --]] function addon:RegisterSettingsSlash(...) -- gotta do this dumb shit because `..., callback` is not valid Lua - local data = {...} + local data = { ... } table.insert(data, function() -- iterate over all categories until we find ours, since OpenToCategory only takes ID local categoryID - local categoryName = C_AddOns.GetAddOnMetadata(addonName, 'Title') + local categoryName = C_AddOns.GetAddOnMetadata(addonName, "Title") for _, category in next, SettingsPanel:GetAllCategories() do if category.name == categoryName then - assert(not categoryID, 'found multiple instances of the same category') + assert(not categoryID, "found multiple instances of the same category") categoryID = category:GetID() end end @@ -406,21 +439,21 @@ function addon:RegisterSettingsSlash(...) addon:RegisterSlash(unpack(data)) end ---[[ namespace:GetOption(_key_) +--[[ namespace:GetOption(_key_) ![](https://img.shields.io/badge/function-blue) Returns the value for the given option `key`. --]] function addon:GetOption(key) - addon:ArgCheck(key, 1, 'string') + addon:ArgCheck(key, 1, "string") assert(addon:AreOptionsLoaded(), "options aren't loaded") assert(_G[self.registeredVariables][key] ~= nil, "key doesn't exist") return _G[self.registeredVariables][key] end ---[[ namespace:SetOption(_key_, _value_) +--[[ namespace:SetOption(_key_, _value_) ![](https://img.shields.io/badge/function-blue) Sets a new `value` to the given options `key`. --]] function addon:SetOption(key, value) - addon:ArgCheck(key, 1, 'string') + addon:ArgCheck(key, 1, "string") assert(addon:AreOptionsLoaded(), "options aren't loaded") assert(_G[self.registeredVariables][key] ~= nil, "key doesn't exist") @@ -428,19 +461,19 @@ function addon:SetOption(key, value) addon:TriggerOptionCallback(key, value) end ---[[ namespace:AreOptionsLoaded() +--[[ namespace:AreOptionsLoaded() ![](https://img.shields.io/badge/function-blue) Checks to see if the savedvariables has been loaded in the game. --]] function addon:AreOptionsLoaded() - return (not not self.registeredVariables) and (not not _G[self.registeredVariables]) + return (not not self.registeredVariables) and not not _G[self.registeredVariables] end ---[[ namespace:RegisterOptionCallback(_key_, _callback_) +--[[ namespace:RegisterOptionCallback(_key_, _callback_) ![](https://img.shields.io/badge/function-blue) Register a `callback` function with the option `key`. --]] function addon:RegisterOptionCallback(key, callback) - addon:ArgCheck(key, 1, 'string') - addon:ArgCheck(callback, 2, 'function') + addon:ArgCheck(key, 1, "string") + addon:ArgCheck(callback, 2, "function") if not self.settingsCallbacks then self.settingsCallbacks = {} @@ -453,11 +486,11 @@ function addon:RegisterOptionCallback(key, callback) table.insert(self.settingsCallbacks[key], callback) end ---[[ namespace:TriggerOptionCallbacks(_key_, _value_) +--[[ namespace:TriggerOptionCallbacks(_key_, _value_) ![](https://img.shields.io/badge/function-blue) Trigger all registered option callbacks for the given `key`, supplying the `value`. --]] function addon:TriggerOptionCallback(key, value) - addon:ArgCheck(key, 1, 'string') + addon:ArgCheck(key, 1, "string") if self.settingsCallbacks and self.settingsCallbacks[key] then for _, callback in next, self.settingsCallbacks[key] do @@ -472,7 +505,7 @@ end do -- sliders aren't supported in menus, so we create our own custom element local function resetSlider(frame) - frame.slider:UnregisterCallback('OnValueChanged', frame) + frame.slider:UnregisterCallback("OnValueChanged", frame) frame.slider:Release() end @@ -482,12 +515,12 @@ do element:AddResetter(resetSlider) end element:AddInitializer(function(frame) - local slider = frame:AttachTemplate('MinimalSliderWithSteppersTemplate') - slider:SetPoint('TOPLEFT', 0, -1) + local slider = frame:AttachTemplate("MinimalSliderWithSteppersTemplate") + slider:SetPoint("TOPLEFT", 0, -1) slider:SetSize(150, 25) - slider:RegisterCallback('OnValueChanged', setter, frame) + slider:RegisterCallback("OnValueChanged", setter, frame) slider:Init(getter(), minValue, maxValue, (maxValue - minValue) / steps, { - [MinimalSliderWithSteppersMixin.Label.Right] = formatter + [MinimalSliderWithSteppersMixin.Label.Right] = formatter, }) frame.slider = slider -- ref for resetter @@ -495,8 +528,8 @@ do -- there's no way to properly reset an element from the menu, so we'll need to use -- a dummy element we can hook OnHide onto -- https://github.com/Stanzilla/WoWUIBugs/issues/652 - local dummy = frame:AttachFrame('Frame') - dummy:SetScript('OnHide', function() + local dummy = frame:AttachFrame("Frame") + dummy:SetScript("OnHide", function() resetSlider(frame) end) end @@ -556,22 +589,22 @@ do -- TODO: menus also has "new feature" flags/textures, see if we can hook into that - Menu.ModifyMenu('MENU_WORLD_MAP_TRACKING', function(_, root) + Menu.ModifyMenu("MENU_WORLD_MAP_TRACKING", function(_, root) root:CreateDivider() - root:CreateTitle((addonName:gsub('(%l)(%u)', '%1 %2')) .. HEADER_COLON) + root:CreateTitle((addonName:gsub("(%l)(%u)", "%1 %2")) .. HEADER_COLON) for _, setting in next, settings do - if setting.type == 'toggle' then + if setting.type == "toggle" then root:CreateCheckbox(setting.title, function() return addon:GetOption(setting.key) end, function() addon:SetOption(setting.key, not addon:GetOption(setting.key)) end) - elseif setting.type == 'slider' then + elseif setting.type == "slider" then local formatter - if type(setting.valueFormat) == 'string' then + if type(setting.valueFormat) == "string" then formatter = GenerateClosure(formatCustom, setting.valueFormat) - elseif type(setting.valueFormat) == 'function' then + elseif type(setting.valueFormat) == "function" then formatter = setting.valueFormat end @@ -580,7 +613,7 @@ do end, function(_, value) addon:SetOption(setting.key, value) end, setting.minValue, setting.maxValue, setting.valueStep or 1, formatter) - elseif setting.type == 'color' then + elseif setting.type == "color" then local value = addon:GetOption(setting.key) local r, g, b, a = addon:CreateColor(value):GetRGBA() root:CreateColorSwatch(setting.title, colorPickerClick, { @@ -593,30 +626,25 @@ do opacity = a, hasOpacity = #value == 8, }) - elseif setting.type == 'menu' then + elseif setting.type == "menu" then local menu = root:CreateButton(setting.title) for _, option in next, setting.options do - menu:CreateRadio( - option.label, - GenerateClosure(menuGetter, setting), - GenerateClosure(menuSetter, setting), - option.value - ) + menu:CreateRadio(option.label, GenerateClosure(menuGetter, setting), GenerateClosure(menuSetter, setting), option.value) end end end end) end - --[[ namespace:RegisterMapSettings(_savedvariable_, _settings_) - Registers a set of `settings` to inject into the world map tracking menu. + --[[ namespace:RegisterMapSettings(_savedvariable_, _settings_) ![](https://img.shields.io/badge/function-blue) + Registers a set of `settings` to inject into the world map tracking menu. The values will be stored by the `settings`' objects' `key` in `savedvariables`. - The `settings` object is identical to the one for [RegisterSetting](namespaceregistersettingssavedvariables-settings). + The `settings` object is identical to the one for [namespace:RegisterSetting()](#namespaceregistersettingssavedvariables-settings-). --]] function addon:RegisterMapSettings(savedvariable, settings) - addon:ArgCheck(savedvariable, 1, 'string') - addon:ArgCheck(settings, 2, 'table') + addon:ArgCheck(savedvariable, 1, "string") + addon:ArgCheck(settings, 2, "table") -- ensure we only add the settings after savedvariables are available to the client local _, isReady = C_AddOns.IsAddOnLoaded(addonName) @@ -624,7 +652,7 @@ do registerMapSettings(savedvariable, settings) else -- don't abuse OnLoad internally - addon:RegisterEvent('ADDON_LOADED', function(_, name) + addon:RegisterEvent("ADDON_LOADED", function(_, name) if name == addonName then registerMapSettings(savedvariable, settings) return true -- unregister diff --git a/LevelUpFX/Libs/Dashi/modules/widgets.lua b/LevelUpFX/Libs/Dashi/modules/widgets.lua index 32734f4..5328d7c 100644 --- a/LevelUpFX/Libs/Dashi/modules/widgets.lua +++ b/LevelUpFX/Libs/Dashi/modules/widgets.lua @@ -1,19 +1,19 @@ local _, addon = ... ---[[ namespace:CreateFrame(_..._) +--[[ namespace:CreateFrame(_..._) ![](https://img.shields.io/badge/function-blue) A wrapper for [`CreateFrame`](https://warcraft.wiki.gg/wiki/API_CreateFrame), mixed in with `namespace.eventMixin`. --]] function addon:CreateFrame(...) return Mixin(CreateFrame(...), addon.eventMixin) end -local KEY_DIRECTION_CVAR = 'ActionButtonUseKeyDown' +local KEY_DIRECTION_CVAR = "ActionButtonUseKeyDown" local function updateKeyDirection(self) if C_CVar.GetCVarBool(KEY_DIRECTION_CVAR) then - self:RegisterForClicks('AnyDown') + self:RegisterForClicks("AnyDown") else - self:RegisterForClicks('AnyUp') + self:RegisterForClicks("AnyUp") end end @@ -23,13 +23,13 @@ local function onCVarUpdate(self, cvar) end end ---[[ namespace:CreateButton(...) +--[[ namespace:CreateButton(...) ![](https://img.shields.io/badge/function-blue) A wrapper for `namespace:CreateFrame(...)`, but will handle key direction preferences of the client. Use this specifically to create clickable buttons. --]] function addon:CreateButton(...) local button = addon:CreateFrame(...) - button:RegisterEvent('CVAR_UPDATE', onCVarUpdate) + button:RegisterEvent("CVAR_UPDATE", onCVarUpdate) -- the CVar doesn't trigger during login, so we'll have to trigger the handlers ourselves onCVarUpdate(button, KEY_DIRECTION_CVAR) @@ -54,9 +54,9 @@ do -- scrollbox provider:SetSortComparator(scroll._sort or defaultSort, true) local view - if scroll.kind == 'list' then + if scroll.kind == "list" then view = CreateScrollBoxListLinearView(scroll._insetTop, scroll._insetBottom, scroll._insetLeft, scroll._insetRight, scroll._spacingHorizontal) - elseif scroll.kind == 'grid' then + elseif scroll.kind == "grid" then local width = scroll:GetWidth() - scroll.bar:GetWidth() - (scroll._insetLeft or 0) - (scroll._insetRight or 0) local stride = math.floor((width - (scroll._spacingHorizontal or 0)) / (scroll._elementWidth + (scroll._spacingHorizontal or 0))) view = CreateScrollBoxListGridView(stride, scroll._insetTop, scroll._insetBottom, scroll._insetLeft, scroll._insetRight, scroll._spacingHorizontal, scroll._spacingVertical) @@ -66,7 +66,7 @@ do -- scrollbox view:SetDataProvider(provider) view:SetElementExtent(scroll._elementHeight) view:SetElementInitializer(scroll._elementType, function(element, data) - if scroll._elementWidth and scroll.kind == 'grid' then + if scroll._elementWidth and scroll.kind == "grid" then element:SetWidth(scroll._elementWidth) end if scroll._elementHeight then @@ -80,8 +80,8 @@ do -- scrollbox for script, callback in next, scroll._scripts do element:SetScript(script, callback) - if script == 'OnEnter' and not scroll._scripts.OnLeave then - element:SetScript('OnLeave', GameTooltip_Hide) + if script == "OnEnter" and not scroll._scripts.OnLeave then + element:SetScript("OnLeave", GameTooltip_Hide) end end end @@ -166,21 +166,21 @@ do -- scrollbox end local function createScrollWidget(parent, kind) - local box = CreateFrame('Frame', nil, parent, 'WowScrollBoxList') - box:SetPoint('TOPLEFT') - box:SetPoint('BOTTOMRIGHT', -8, 0) -- offset to not overlap scrollbar + local box = CreateFrame("Frame", nil, parent, "WowScrollBoxList") + box:SetPoint("TOPLEFT") + box:SetPoint("BOTTOMRIGHT", -8, 0) -- offset to not overlap scrollbar box.kind = kind - local bar = CreateFrame('EventFrame', nil, parent, 'MinimalScrollBar') - bar:SetPoint('TOPLEFT', box, 'TOPRIGHT') - bar:SetPoint('BOTTOMLEFT', box, 'BOTTOMRIGHT') + local bar = CreateFrame("EventFrame", nil, parent, "MinimalScrollBar") + bar:SetPoint("TOPLEFT", box, "TOPRIGHT") + bar:SetPoint("BOTTOMLEFT", box, "BOTTOMRIGHT") box.bar = bar return Mixin(box, scrollMixin) end - --[[ namespace:CreateScrollList(_parent_) - Creates and returns a scroll box with scroll bar and a data provider in a list representation. + --[[ namespace:CreateScrollList(_parent_) ![](https://img.shields.io/badge/function-blue) + Creates and returns a scroll box with scroll bar and a data provider in a list representation. It gets automatically sized to fill the space of the parent. It provides the following methods, and is initialized whenever data is provided, so do that last. @@ -201,10 +201,10 @@ do -- scrollbox * `list:ResetData()` --]] function addon:CreateScrollList(parent) - return createScrollWidget(parent, 'list') + return createScrollWidget(parent, "list") end - --[[ namespace:CreateScrollGrid(_parent_) + --[[ namespace:CreateScrollGrid(_parent_) ![](https://img.shields.io/badge/function-blue) Creates and returns a scroll box with scroll bar and a data provider in a grid representation. It gets automatically sized to fill the space of the parent. @@ -228,6 +228,6 @@ do -- scrollbox * `grid:ResetData()` --]] function addon:CreateScrollGrid(parent) - return createScrollWidget(parent, 'grid') + return createScrollWidget(parent, "grid") end end