diff --git a/CHANGELOG.md b/CHANGELOG.md index 97cd242..33972f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Upcoming] Version 0.8.0-beta - 2020-06-08 ### Added - handle replacement via slash command +- emote detection + +## Version 0.7.2-beta - 2020-06-08 +### Added +- minimap button +### Changed +- graphical move arrows +###Fixed +- crash on matches with 0-width ## Version 0.7.1-beta - 2020-06-07 ### Added diff --git a/Grichelde.lua b/Grichelde.lua index 1a5dd07..54b6fe0 100644 --- a/Grichelde.lua +++ b/Grichelde.lua @@ -35,10 +35,10 @@ function Grichelde:OnInitialize() self:UpgradeDatabase() self.options, self.dialog = self:SetupOptions() - - -- populate UI from database self:RefreshOptions("OnProfileChanged") + self.ldb, self.icon = self:MinimapButton() + self:SetupSlashCommands() end @@ -63,15 +63,16 @@ end function Grichelde:HandleSlashCommand(input) -- Show the GUI if no input is supplied, otherwise handle the chat input. if self.functions.nilOrEmpty(input) then - LibStub("AceConfigDialog-3.0"):Open(self.name) + self:OpenOptions() else -- handle slash ourselves self:DebugPrint("Handle slash command: " .. input) if input == "mappings" then - self:ShowMappings() - end - if input == "options" then + self:ToogleMappings() + elseif input == "options" then self:PrintOptions() + elseif input == "profile" then + self:PrintProfile() end end end \ No newline at end of file diff --git a/Grichelde.toc b/Grichelde.toc index 340c47c..1a1b823 100644 --- a/Grichelde.toc +++ b/Grichelde.toc @@ -3,7 +3,7 @@ ## Title: Grichelde ## Notes: Replaces characters from the chat box ## Notes-de: Ersetzt eingegebene Zeichen in der Chat-Zeile -## Version: 0.7.1-beta +## Version: 0.7.2-beta ## Author: Teilzeit-Jedi ## eMail: tj@teilzeit-jedi.de @@ -11,10 +11,10 @@ ## X-Curse-Project-ID: 385480 ## X-License: GPLv3 ## X-Category: Chat/Communication -## X-Credits: Teilzeit-Jedi, Nathan Pieper +## X-Credits: Teilzeit-Jedi ## X-Embeds: Ace3 -## OptionalDeps: Ace3 +## OptionalDeps: Ace3, LibDataBroker, LibIcon ## SavedVariables: GricheldeDB libs.xml diff --git a/GricheldeChat.lua b/GricheldeChat.lua index 9f78052..96c8a08 100644 --- a/GricheldeChat.lua +++ b/GricheldeChat.lua @@ -2,8 +2,8 @@ local _G = _G local Grichelde = _G.Grichelde -local nilOrEmpty, ipairs, spairs, tContains, tFilter, tInsert, tConcat, find, sub, isUpper, isLower, toUpper, toLower, trim, length - = Grichelde.functions.nilOrEmpty, Grichelde.functions.ipairs, Grichelde.functions.spairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, +local IsAddOnLoaded, nilOrEmpty, ipairs, spairs, tContains, tFilter, tInsert, tConcat, find, sub, isUpper, isLower, toUpper, toLower, trim, length + = Grichelde.functions.IsAddOnLoaded, Grichelde.functions.nilOrEmpty, Grichelde.functions.ipairs, Grichelde.functions.spairs, Grichelde.functions.tContains, Grichelde.functions.tFilter, Grichelde.functions.tInsert, Grichelde.functions.tConcat, Grichelde.functions.find, Grichelde.functions.sub, Grichelde.functions.isUpper, Grichelde.functions.isLower, Grichelde.functions.toUpper, Grichelde.functions.toLower, Grichelde.functions.trim, Grichelde.functions.length --- Before a chat message is sent, check if replacement is required and replace the text accordingly. @@ -27,7 +27,7 @@ end function Grichelde:CheckAndReplace(message, type) local text = message if (self:CheckReplacement(text, type)) then - if (_G.Misspelled) then + if (IsAddOnLoaded("Misspelled")) then self:DebugPrint("Misspelled detected: cleansing message") text = _G.Misspelled:RemoveHighlighting(text) end @@ -218,7 +218,7 @@ function Grichelde:ReplaceCharacters(text) local oldResult = result local pos1, pos2 = find(oldResult, search, pos) - while (pos1 and pos2) do + while (pos1 and pos2 and pos1 <= pos2) do self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) local pre = sub(result, 1, pos1 - 1 + offset) local post = sub(result, pos2 + 1 + offset) @@ -247,7 +247,7 @@ function Grichelde:ReplaceCharacters(text) local lowerSearch = toLower(search) local pos1, pos2 = find(lowerResult, lowerSearch, pos) - while (pos1 and pos2) do + while (pos1 and pos2 and pos1 <= pos2) do self:TracePrint("pos1: %d, pos2: %d", pos1, pos2) local pre = sub(result, 1, pos1 - 1 + offset) local match = sub(result, pos1 + offset, pos2 + offset) diff --git a/GricheldeConstants.lua b/GricheldeConstants.lua index b60c661..5f34dc9 100644 --- a/GricheldeConstants.lua +++ b/GricheldeConstants.lua @@ -9,7 +9,17 @@ Grichelde.LOG_LEVEL.TRACE = 2 Grichelde.MAPPING_OFFSET = 10 +Grichelde.ICONS = {} +Grichelde.ICONS.MOVE_UP = "Interface\\MainMenuBar\\UI-MainMenu-ScrollUpButton-Up" +Grichelde.ICONS.MOVE_DOWN = "Interface\\MainMenuBar\\UI-MainMenu-ScrollDownButton-Up" + -- colors: +Grichelde.COLORS = {} +Grichelde.COLORS.NORMAL = _G.NORMAL_FONT_COLOR +Grichelde.COLORS.HIGHLIGHT = _G.HIGHLIGHT_FONT_COLOR +Grichelde.COLORS.RED = _G.RED_FONT_COLOR +Grichelde.COLORS.GREEN = _G.GREEN_FONT_COLOR + Grichelde.COLOR_CODES = {} Grichelde.COLOR_CODES.PREFIX = "|c00FFAA00" -- https://github.com/stoneharry/Misc-WoW-Stuff/blob/master/EoC%20Interface/FrameXML/Constants.lua @@ -120,42 +130,43 @@ end -- faster function lookups by mapping to local refs Grichelde.functions = {} -Grichelde.functions.type = _G.type -Grichelde.functions.print = _G.print -Grichelde.functions.nilOrEmpty = nilOrEmpty -Grichelde.functions.pairs = _G.pairs -Grichelde.functions.ipairs = _G.ipairs -Grichelde.functions.spairs = spairs -Grichelde.functions.tContains = _G.tContains -Grichelde.functions.tFilter = tFilter -Grichelde.functions.tInsert = _G.table.insert -Grichelde.functions.tConcat = _G.table.concat -Grichelde.functions.tSize = tSize -Grichelde.functions.tSort = _G.table.sort -Grichelde.functions.tClone = tClone -Grichelde.functions.tNext = _G.next -Grichelde.functions.setmetatable = _G.setmetatable -Grichelde.functions.getmetatable = _G.getmetatable -Grichelde.functions.select = _G.select -Grichelde.functions.unpack = _G.unpack -Grichelde.functions.find = _G.string.find -Grichelde.functions.sub = _G.string.sub -Grichelde.functions.gsub = _G.string.gsub -Grichelde.functions.match = _G.strmatch -Grichelde.functions.join = _G.strjoin -Grichelde.functions.split = _G.strsplit -Grichelde.functions.toUpper = _G.strupper -Grichelde.functions.toLower = _G.strlower -Grichelde.functions.isChar = isChar -Grichelde.functions.isNumber = isNumber -Grichelde.functions.isUpper = isUpper -Grichelde.functions.isLower = isLower -Grichelde.functions.isCapital = isCapital -Grichelde.functions.format = _G.string.format -Grichelde.functions.rep = _G.string.rep -Grichelde.functions.trim = _G.strtrim -Grichelde.functions.length = _G.string.len -Grichelde.functions.toString = _G.tostring -Grichelde.functions.toNumber = _G.tonumber -Grichelde.functions.max = _G.math.max -Grichelde.functions.min = _G.math.min \ No newline at end of file +Grichelde.functions.IsAddOnLoaded = _G.IsAddOnLoaded +Grichelde.functions.type = _G.type +Grichelde.functions.print = _G.print +Grichelde.functions.nilOrEmpty = nilOrEmpty +Grichelde.functions.pairs = _G.pairs +Grichelde.functions.ipairs = _G.ipairs +Grichelde.functions.spairs = spairs +Grichelde.functions.tContains = _G.tContains +Grichelde.functions.tFilter = tFilter +Grichelde.functions.tInsert = _G.table.insert +Grichelde.functions.tConcat = _G.table.concat +Grichelde.functions.tSize = tSize +Grichelde.functions.tSort = _G.table.sort +Grichelde.functions.tClone = tClone +Grichelde.functions.tNext = _G.next +Grichelde.functions.setmetatable = _G.setmetatable +Grichelde.functions.getmetatable = _G.getmetatable +Grichelde.functions.select = _G.select +Grichelde.functions.unpack = _G.unpack +Grichelde.functions.find = _G.string.find +Grichelde.functions.sub = _G.string.sub +Grichelde.functions.gsub = _G.string.gsub +Grichelde.functions.match = _G.strmatch +Grichelde.functions.join = _G.strjoin +Grichelde.functions.split = _G.strsplit +Grichelde.functions.toUpper = _G.strupper +Grichelde.functions.toLower = _G.strlower +Grichelde.functions.isChar = isChar +Grichelde.functions.isNumber = isNumber +Grichelde.functions.isUpper = isUpper +Grichelde.functions.isLower = isLower +Grichelde.functions.isCapital = isCapital +Grichelde.functions.format = _G.string.format +Grichelde.functions.rep = _G.string.rep +Grichelde.functions.trim = _G.strtrim +Grichelde.functions.length = _G.string.len +Grichelde.functions.toString = _G.tostring +Grichelde.functions.toNumber = _G.tonumber +Grichelde.functions.max = _G.math.max +Grichelde.functions.min = _G.math.min \ No newline at end of file diff --git a/GricheldeDatabase.lua b/GricheldeDatabase.lua index 19eab1f..b66a148 100644 --- a/GricheldeDatabase.lua +++ b/GricheldeDatabase.lua @@ -10,6 +10,9 @@ function Grichelde:GetDefaultConfig() global = {}, profile = { enabled = true, + minimapButton = { + hide = false + }, channels = { ["*"] = false, say = true, diff --git a/GricheldeOptions.lua b/GricheldeOptions.lua index 00855d5..5e505ea 100644 --- a/GricheldeOptions.lua +++ b/GricheldeOptions.lua @@ -22,7 +22,15 @@ function Grichelde:CreateOptionsUI() desc = self:Format(self.L.Options_Enabled_Desc, self.L.AddonName), disabled = false, }, - + minimapButton = { + order = 1, + type = "toggle", + name = self.L.Options_Minimap_Button_Name, + desc = self.L.Options_Minimap_Button_Desc, + set = function(info, val) self:ToogleMinimapButton(info, val) end, + get = function(info) return not self.db.profile.minimapButton.hide end, + disabled = false, + }, channels = { order = 2, type = "group", @@ -184,28 +192,28 @@ function Grichelde:CreateMapping(offset) moveUp = { order = 10, type = "execute", - --name = self.L.Options_Mapping_MoveUp_Name, + -- name = self.L.Options_Mapping_MoveUp_Name, name = "", desc = self.L.Options_Mapping_MoveUp_Desc, - image = "Interface\\ChatFrame\\UI-ChatIcon-ScrollUp-Up", - width = 0.25, + image = Grichelde.ICONS.MOVE_UP, + width = 0.15, func = function(info) self:MoveUp(info) end }, moveDown = { order = 11, type = "execute", - --name = self.L.Options_Mapping_MoveDown_Name, + -- name = self.L.Options_Mapping_MoveDown_Name, name = "", desc = self.L.Options_Mapping_MoveDown_Desc, - image = "Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up", - width = 0.25, + image = Grichelde.ICONS.MOVE_DOWN, + width = 0.15, func = function(info) self:MoveDown(info) end }, spacer = { order = 18, type = "description", name = "", - width = 1, + width = 1.2, }, delete = { order = 19, @@ -221,14 +229,6 @@ function Grichelde:CreateMapping(offset) } end ---dropButton:SetWidth(24) ---dropButton:SetHeight(24) ---dropButton:SetPoint("TOPRIGHT", DRight, "TOPRIGHT", -16, -18) ---dropButton:SetNormalTexture([[Interface\ChatFrame\UI-ChatIcon-ScrollDown-Up]]) ---dropButton:SetPushedTexture([[Interface\ChatFrame\UI-ChatIcon-ScrollDown-Down]]) ---dropButton:SetDisabledTexture([[Interface\ChatFrame\UI-ChatIcon-ScrollDown-Disabled]]) ---dropButton:SetHighlightTexture([[Interface\Buttons\UI-Common-MouseHilight]], "ADD") - function Grichelde:SetupOptions() -- add DB-backed profiles to UI options local options = self:CreateOptionsUI() @@ -243,25 +243,6 @@ function Grichelde:SetupOptions() return options, dialog end -function Grichelde:IsDisabled(info) - if info.option.type == "group" then - return false - end - return not self.db.profile.enabled -end - -function Grichelde:MappingName(info) --- self:TracePrint("MappingName : info") --- self:TracePrint(info) - local option = self.db.profile.replacements[info[2]] - - if nilOrEmpty(option.searchText) and nilOrEmpty(option.replaceText) then - return self.L.Options_Mapping_EmptyMapping - else - return self:Format(self.L.Options_Mapping_Group_Name, option.searchText or "", option.replaceText or "") - end -end - function Grichelde:RefreshOptions(event) self:DebugPrint("RefreshOptions : event:", event) if event == "OnNewProfile" then @@ -282,6 +263,87 @@ function Grichelde:RefreshOptions(event) self:RefreshReplacements(self.db.profile.replacements) end +--- add Minimap button +function Grichelde:MinimapButton() + local function toggleOptionsUI(_, button) + if button == 'LeftButton' then + self:ToggleOptions() + elseif button == 'RightButton' then + self:ToogleMappings() + end + end + + local function tooltip(tooltip) + if not tooltip or not tooltip.AddLine then return end + tooltip:SetText(self.L.AddonName, + Grichelde.COLORS.HIGHLIGHT.r, Grichelde.COLORS.HIGHLIGHT.g, Grichelde.COLORS.HIGHLIGHT.b, Grichelde.COLORS.HIGHLIGHT.a + ) + + tooltip:AddDoubleLine(self.L.Options_Minimap_Tooltip_Options_Left, self.L.Options_Minimap_Tooltip_Options_Right, + Grichelde.COLORS.GREEN.r, Grichelde.COLORS.GREEN.g, Grichelde.COLORS.GREEN.b, Grichelde.COLORS.GREEN.a, + Grichelde.COLORS.NORMAL.r, Grichelde.COLORS.NORMAL.g, Grichelde.COLORS.NORMAL.b, Grichelde.COLORS.NORMAL.a + ) + tooltip:AddDoubleLine(self.L.Options_Minimap_Tooltip_Mappings_Left, self.L.Options_Minimap_Tooltip_Mappings_Right, + Grichelde.COLORS.GREEN.r, Grichelde.COLORS.GREEN.g, Grichelde.COLORS.GREEN.b, Grichelde.COLORS.GREEN.a, + Grichelde.COLORS.NORMAL.r, Grichelde.COLORS.NORMAL.g, Grichelde.COLORS.NORMAL.b, Grichelde.COLORS.NORMAL.a + ) + end + + local ldb = LibStub("LibDataBroker-1.1"):NewDataObject(self.name, { + type = "launcher", + text = self.AddonName, + icon = "Interface\\Icons\\Spell_Holy_Silence", + --icon = ([[Interface\Addons\%s\%s]]):format(self.name, self.name), + OnClick = toggleOptionsUI, + OnRightClick = function() self:ShowMappings() end, + OnTooltipShow = tooltip, + }) + + local icon = LibStub("LibDBIcon-1.0") + self:DebugPrint("MinimapButton : hidden: ", self.db.profile.minimapButton.hide) + icon:Register(self.name, ldb, self.db.profile.minimapButton) + + return ldb, icon +end + + +function Grichelde:ToggleOptions() + self:DebugPrint("MinimapButton : options open: ", self.dialog.OpenFrames[self.name]) + if self.dialog.OpenFrames[self.name] then + self:CloseOptions() + else + self:OpenOptions() + end +end + +function Grichelde:OpenOptions() + self.dialog:Open(self.name) +end + +function Grichelde:CloseOptions() + self.dialog:Close(self.name) +end + +function Grichelde:IsDisabled(info) + if info.option.type == "group" then + return false + end + return not self.db.profile.enabled +end + +function Grichelde:MappingName(info) +-- self:TracePrint("MappingName : info") +-- self:TracePrint(info) + local option = self.db.profile.replacements[info[2]] + + if nilOrEmpty(option.searchText) and nilOrEmpty(option.replaceText) then + return self.L.Options_Mapping_EmptyMapping + else + return self:Format(self.L.Options_Mapping_Group_Name, option.searchText or "", option.replaceText or "") + end +end + + --- Create UI options for rhe given replacement table (from DB). --- Usually called with with self.db.profile.replacements -- @param replacementsTable @@ -440,3 +502,21 @@ function Grichelde:DeleteAllMappings() self:RefreshOptions("DeleteAllMappings") end + +function Grichelde:ToogleMinimapButton(info, val) + self:TracePrint("ToogleMinimapButton : info") + for i = 0, #info do + self:TracePrint("%d = %s", i, info[i]) + end + + self.db.profile.minimapButton.hide = not val + self:DebugPrint("ToogleMinimapButton : hidden: ", self.db.profile.minimapButton.hide) + + if self.db.profile.minimapButton.hide then + self.icon:Hide(self.name); + else + self.icon:Show(self.name); + end +end + + diff --git a/GricheldeUpgrade.lua b/GricheldeUpgrade.lua index db4c289..c91ff88 100644 --- a/GricheldeUpgrade.lua +++ b/GricheldeUpgrade.lua @@ -8,7 +8,7 @@ function Grichelde:Upgrade_To_v060() self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.6.0" .. Grichelde.COLOR_CODES.CLOSE) local replacements = self.db.profile.replacements or {} - self:DebugPrint("Upgrade_To_060 : old database") + self:DebugPrint("Upgrade_To_v060 : old database") self:DebugPrint(replacements) for _, replTable in pairs(replacements) do @@ -16,7 +16,7 @@ function Grichelde:Upgrade_To_v060() replTable["caseSensitive"] = nil end - self:DebugPrint("Upgrade_To_060 : new database") + self:DebugPrint("Upgrade_To_v060 : new database") self:DebugPrint(replacements) return 0, 6, 0 end @@ -25,7 +25,7 @@ function Grichelde:Upgrade_To_v070() self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.7.0" .. Grichelde.COLOR_CODES.CLOSE) local replacements = self.db.profile.replacements or {} - self:DebugPrint("Upgrade_To_070 : old database") + self:DebugPrint("Upgrade_To_v070 : old database") self:DebugPrint(replacements) for _, replTable in pairs(replacements) do @@ -33,16 +33,23 @@ function Grichelde:Upgrade_To_v070() replTable["ignoreCase"] = nil end - self:DebugPrint("Upgrade_To_070 : new database") + self:DebugPrint("Upgrade_To_v070 : new database") self:DebugPrint(replacements) return 0, 7, 0 end ---[[ -function Grichelde:Upgrade_To_v071() - return 0, 7, 1 +function Grichelde:Upgrade_To_v072() + self:PrefixedPrint(self.L.Upgrade_ToVersion, Grichelde.COLOR_CODES.ORANGE .. "0.7.2" .. Grichelde.COLOR_CODES.CLOSE) + + self:DebugPrint("Upgrade_To_v072 : old database") + self:DebugPrint(self.db.profile) + + self.db.profile["minimapButton"] = { hide = false } + + self:DebugPrint("Upgrade_To_v072 : new database") + self:DebugPrint(self.db.profile) + return 0, 7, 2 end -]] function Grichelde:UpgradeDatabase() local dbVersion = self.db.global.version or "0.0.0" @@ -63,14 +70,12 @@ function Grichelde:UpgradeDatabase() upgrade = upgrade + 1 major, minor, patch = self:Upgrade_To_v070(dbVersion) end ---[[ if minor == 7 then - if patch < 1 then + if patch < 2 then upgrade = upgrade + 1 - major, minor, patch = self:Upgrade_To_v71(dbVersion) + major, minor, patch = self:Upgrade_To_v072(dbVersion) end end -]] end if upgrade == 0 then diff --git a/GricheldeUtils.lua b/GricheldeUtils.lua index 2344df7..1c6dd92 100644 --- a/GricheldeUtils.lua +++ b/GricheldeUtils.lua @@ -172,6 +172,14 @@ function Grichelde:PrintOptions() end, self.options.args.replacements.args) end +--- Print DB +function Grichelde:PrintProfile() + self:PrefixedPrint(self.COLOR_CODES.PREFIX .. self.L.Debug_Profile .. self.COLOR_CODES.CLOSE) + self:LogPrint(-1, function(...) + print(self.COLOR_CODES.PREFIX .. self.L.AddonName .. self.COLOR_CODES.CLOSE .. ":", self:Format(...)) + end, self.db.profile) +end + --- Print DB replacements to chat frame function Grichelde:PrintMappings() self:PrefixedPrint(self.COLOR_CODES.PREFIX .. self.L.Debug_Mappings .. self.COLOR_CODES.CLOSE) @@ -181,9 +189,11 @@ function Grichelde:PrintMappings() end --- Open UI windows with replacements in it -function Grichelde:ShowMappings() - if self.debugFrame and self.debugFrame:IsShown() then - self.debugFrame:Release(self.debugFrame) +function Grichelde:ToogleMappings() + local AceGUI = LibStub("AceGUI-3.0") + + if self.debugFrame then + AceGUI:Release(self.debugFrame) self.debugFrame = nil else local replacements = self.db.profile.replacements or {} @@ -194,28 +204,40 @@ function Grichelde:ShowMappings() end end - local AceGUI = LibStub("AceGUI-3.0") local frame = AceGUI:Create("Frame"); frame:SetTitle(self.L.Debug_Mappings); frame:SetStatusText(self:Format(self.L.Debug_Mappings_Found, repls)) frame:SetWidth(400); - frame:SetHeight(600); - frame:SetLayout("Fill"); - - local function closeFrame() frame:Release(frame) end + frame:SetAutoAdjustHeight(true) + --frame:SetFullHeight(true) + frame:SetLayout("Flow"); + local function closeFrame(widget) AceGUI:Release(widget); self.debugFrame = nil end frame:SetCallback("OnClose", closeFrame) + local hint = AceGUI:Create("Label"); + hint:SetText(self.L.Debug_Mappings_Hint) + hint:SetFullWidth(true) + frame:AddChild(hint); + + local scroll = AceGUI:Create("ScrollFrame"); + scroll:SetFullWidth(true) + scroll:SetFullHeight(true) + scroll:SetLayout("Fill"); + local configBox = AceGUI:Create("MultiLineEditBox"); + configBox:SetLabel("") local text = "" tPrint(replacements, 0, {}, function(s) text = text .. s .. "|n" end) - configBox:SetLabel("") configBox:SetText(text) + configBox:SetFullWidth(true) + configBox:SetFullHeight(true) configBox:DisableButton(true) --configBox:SetDisabled(true) - configBox:SetNumLines(30); + configBox:SetNumLines(50); configBox:SetFocus() + scroll:AddChild(configBox); - frame:AddChild(configBox); + frame:AddChild(scroll); self.debugFrame = frame end end \ No newline at end of file diff --git a/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua new file mode 100644 index 0000000..e865bdf --- /dev/null +++ b/Libs/LibDBIcon-1.0/LibDBIcon-1.0.lua @@ -0,0 +1,470 @@ + +----------------------------------------------------------------------- +-- LibDBIcon-1.0 +-- +-- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays. +-- + +local DBICON10 = "LibDBIcon-1.0" +local DBICON10_MINOR = 43 -- Bump on changes +if not LibStub then error(DBICON10 .. " requires LibStub.") end +local ldb = LibStub("LibDataBroker-1.1", true) +if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end +local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR) +if not lib then return end + +lib.objects = lib.objects or {} +lib.callbackRegistered = lib.callbackRegistered or nil +lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib) +lib.notCreated = lib.notCreated or {} +lib.radius = lib.radius or 5 +lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate") +local next, Minimap = next, Minimap +local isDraggingButton = false + +function lib:IconCallback(event, name, key, value) + if lib.objects[name] then + if key == "icon" then + lib.objects[name].icon:SetTexture(value) + elseif key == "iconCoords" then + lib.objects[name].icon:UpdateCoord() + elseif key == "iconR" then + local _, g, b = lib.objects[name].icon:GetVertexColor() + lib.objects[name].icon:SetVertexColor(value, g, b) + elseif key == "iconG" then + local r, _, b = lib.objects[name].icon:GetVertexColor() + lib.objects[name].icon:SetVertexColor(r, value, b) + elseif key == "iconB" then + local r, g = lib.objects[name].icon:GetVertexColor() + lib.objects[name].icon:SetVertexColor(r, g, value) + end + end +end +if not lib.callbackRegistered then + ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback") + ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback") + ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback") + ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback") + ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback") + lib.callbackRegistered = true +end + +local function getAnchors(frame) + local x, y = frame:GetCenter() + if not x or not y then return "CENTER" end + local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or "" + local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM" + return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf +end + +local function onEnter(self) + if isDraggingButton then return end + + for _, button in next, lib.objects do + if button.showOnMouseover then + button.fadeOut:Stop() + button:SetAlpha(1) + end + end + + local obj = self.dataObject + if obj.OnTooltipShow then + lib.tooltip:SetOwner(self, "ANCHOR_NONE") + lib.tooltip:SetPoint(getAnchors(self)) + obj.OnTooltipShow(lib.tooltip) + lib.tooltip:Show() + elseif obj.OnEnter then + obj.OnEnter(self) + end +end + +local function onLeave(self) + lib.tooltip:Hide() + + if not isDraggingButton then + for _, button in next, lib.objects do + if button.showOnMouseover then + button.fadeOut:Play() + end + end + end + + local obj = self.dataObject + if obj.OnLeave then + obj.OnLeave(self) + end +end + +-------------------------------------------------------------------------------- + +local onDragStart, updatePosition + +do + local minimapShapes = { + ["ROUND"] = {true, true, true, true}, + ["SQUARE"] = {false, false, false, false}, + ["CORNER-TOPLEFT"] = {false, false, false, true}, + ["CORNER-TOPRIGHT"] = {false, false, true, false}, + ["CORNER-BOTTOMLEFT"] = {false, true, false, false}, + ["CORNER-BOTTOMRIGHT"] = {true, false, false, false}, + ["SIDE-LEFT"] = {false, true, false, true}, + ["SIDE-RIGHT"] = {true, false, true, false}, + ["SIDE-TOP"] = {false, false, true, true}, + ["SIDE-BOTTOM"] = {true, true, false, false}, + ["TRICORNER-TOPLEFT"] = {false, true, true, true}, + ["TRICORNER-TOPRIGHT"] = {true, false, true, true}, + ["TRICORNER-BOTTOMLEFT"] = {true, true, false, true}, + ["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false}, + } + + local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min + function updatePosition(button, position) + local angle = rad(position or 225) + local x, y, q = cos(angle), sin(angle), 1 + if x < 0 then q = q + 1 end + if y > 0 then q = q + 2 end + local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND" + local quadTable = minimapShapes[minimapShape] + local w = (Minimap:GetWidth() / 2) + lib.radius + local h = (Minimap:GetHeight() / 2) + lib.radius + if quadTable[q] then + x, y = x*w, y*h + else + local diagRadiusW = sqrt(2*(w)^2)-10 + local diagRadiusH = sqrt(2*(h)^2)-10 + x = max(-w, min(x*diagRadiusW, w)) + y = max(-h, min(y*diagRadiusH, h)) + end + button:SetPoint("CENTER", Minimap, "CENTER", x, y) + end +end + +local function onClick(self, b) + if self.dataObject.OnClick then + self.dataObject.OnClick(self, b) + end +end + +local function onMouseDown(self) + self.isMouseDown = true + self.icon:UpdateCoord() +end + +local function onMouseUp(self) + self.isMouseDown = false + self.icon:UpdateCoord() +end + +do + local deg, atan2 = math.deg, math.atan2 + local function onUpdate(self) + local mx, my = Minimap:GetCenter() + local px, py = GetCursorPosition() + local scale = Minimap:GetEffectiveScale() + px, py = px / scale, py / scale + local pos = 225 + if self.db then + pos = deg(atan2(py - my, px - mx)) % 360 + self.db.minimapPos = pos + else + pos = deg(atan2(py - my, px - mx)) % 360 + self.minimapPos = pos + end + updatePosition(self, pos) + end + + function onDragStart(self) + self:LockHighlight() + self.isMouseDown = true + self.icon:UpdateCoord() + self:SetScript("OnUpdate", onUpdate) + isDraggingButton = true + lib.tooltip:Hide() + for _, button in next, lib.objects do + if button.showOnMouseover then + button.fadeOut:Stop() + button:SetAlpha(1) + end + end + end +end + +local function onDragStop(self) + self:SetScript("OnUpdate", nil) + self.isMouseDown = false + self.icon:UpdateCoord() + self:UnlockHighlight() + isDraggingButton = false + for _, button in next, lib.objects do + if button.showOnMouseover then + button.fadeOut:Play() + end + end +end + +local defaultCoords = {0, 1, 0, 1} +local function updateCoord(self) + local coords = self:GetParent().dataObject.iconCoords or defaultCoords + local deltaX, deltaY = 0, 0 + if not self:GetParent().isMouseDown then + deltaX = (coords[2] - coords[1]) * 0.05 + deltaY = (coords[4] - coords[3]) * 0.05 + end + self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY) +end + +local function createButton(name, object, db) + local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap) + button.dataObject = object + button.db = db + button:SetFrameStrata("MEDIUM") + button:SetSize(31, 31) + button:SetFrameLevel(8) + button:RegisterForClicks("anyUp") + button:RegisterForDrag("LeftButton") + button:SetHighlightTexture(136477) --"Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight" + local overlay = button:CreateTexture(nil, "OVERLAY") + overlay:SetSize(53, 53) + overlay:SetTexture(136430) --"Interface\\Minimap\\MiniMap-TrackingBorder" + overlay:SetPoint("TOPLEFT") + local background = button:CreateTexture(nil, "BACKGROUND") + background:SetSize(20, 20) + background:SetTexture(136467) --"Interface\\Minimap\\UI-Minimap-Background" + background:SetPoint("TOPLEFT", 7, -5) + local icon = button:CreateTexture(nil, "ARTWORK") + icon:SetSize(17, 17) + icon:SetTexture(object.icon) + icon:SetPoint("TOPLEFT", 7, -6) + button.icon = icon + button.isMouseDown = false + + local r, g, b = icon:GetVertexColor() + icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b) + + icon.UpdateCoord = updateCoord + icon:UpdateCoord() + + button:SetScript("OnEnter", onEnter) + button:SetScript("OnLeave", onLeave) + button:SetScript("OnClick", onClick) + if not db or not db.lock then + button:SetScript("OnDragStart", onDragStart) + button:SetScript("OnDragStop", onDragStop) + end + button:SetScript("OnMouseDown", onMouseDown) + button:SetScript("OnMouseUp", onMouseUp) + + button.fadeOut = button:CreateAnimationGroup() + local animOut = button.fadeOut:CreateAnimation("Alpha") + animOut:SetOrder(1) + animOut:SetDuration(0.2) + animOut:SetFromAlpha(1) + animOut:SetToAlpha(0) + animOut:SetStartDelay(1) + button.fadeOut:SetToFinalAlpha(true) + + lib.objects[name] = button + + if lib.loggedIn then + updatePosition(button, db and db.minimapPos) + if not db or not db.hide then + button:Show() + else + button:Hide() + end + end + lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback +end + +-- We could use a metatable.__index on lib.objects, but then we'd create +-- the icons when checking things like :IsRegistered, which is not necessary. +local function check(name) + if lib.notCreated[name] then + createButton(name, lib.notCreated[name][1], lib.notCreated[name][2]) + lib.notCreated[name] = nil + end +end + +-- Wait a bit with the initial positioning to let any GetMinimapShape addons +-- load up. +if not lib.loggedIn then + local f = CreateFrame("Frame") + f:SetScript("OnEvent", function(f) + for _, button in next, lib.objects do + updatePosition(button, button.db and button.db.minimapPos) + if not button.db or not button.db.hide then + button:Show() + else + button:Hide() + end + end + lib.loggedIn = true + f:SetScript("OnEvent", nil) + end) + f:RegisterEvent("PLAYER_LOGIN") +end + +local function getDatabase(name) + return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db +end + +function lib:Register(name, object, db) + if not object.icon then error("Can't register LDB objects without icons set!") end + if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end + if not db or not db.hide then + createButton(name, object, db) + else + lib.notCreated[name] = {object, db} + end +end + +function lib:Lock(name) + if not lib:IsRegistered(name) then return end + if lib.objects[name] then + lib.objects[name]:SetScript("OnDragStart", nil) + lib.objects[name]:SetScript("OnDragStop", nil) + end + local db = getDatabase(name) + if db then + db.lock = true + end +end + +function lib:Unlock(name) + if not lib:IsRegistered(name) then return end + if lib.objects[name] then + lib.objects[name]:SetScript("OnDragStart", onDragStart) + lib.objects[name]:SetScript("OnDragStop", onDragStop) + end + local db = getDatabase(name) + if db then + db.lock = nil + end +end + +function lib:Hide(name) + if not lib.objects[name] then return end + lib.objects[name]:Hide() +end + +function lib:Show(name) + check(name) + local button = lib.objects[name] + if button then + button:Show() + updatePosition(button, button.db and button.db.minimapPos or button.minimapPos) + end +end + +function lib:IsRegistered(name) + return (lib.objects[name] or lib.notCreated[name]) and true or false +end + +function lib:Refresh(name, db) + check(name) + local button = lib.objects[name] + if db then + button.db = db + end + updatePosition(button, button.db and button.db.minimapPos or button.minimapPos) + if not button.db or not button.db.hide then + button:Show() + else + button:Hide() + end + if not button.db or not button.db.lock then + button:SetScript("OnDragStart", onDragStart) + button:SetScript("OnDragStop", onDragStop) + else + button:SetScript("OnDragStart", nil) + button:SetScript("OnDragStop", nil) + end +end + +function lib:GetMinimapButton(name) + return lib.objects[name] +end + +do + local function OnMinimapEnter() + if isDraggingButton then return end + for _, button in next, lib.objects do + if button.showOnMouseover then + button.fadeOut:Stop() + button:SetAlpha(1) + end + end + end + local function OnMinimapLeave() + if isDraggingButton then return end + for _, button in next, lib.objects do + if button.showOnMouseover then + button.fadeOut:Play() + end + end + end + Minimap:HookScript("OnEnter", OnMinimapEnter) + Minimap:HookScript("OnLeave", OnMinimapLeave) + + function lib:ShowOnEnter(name, value) + local button = lib.objects[name] + if button then + if value then + button.showOnMouseover = true + button.fadeOut:Stop() + button:SetAlpha(0) + else + button.showOnMouseover = false + button.fadeOut:Stop() + button:SetAlpha(1) + end + end + end +end + +function lib:GetButtonList() + local t = {} + for name in next, lib.objects do + t[#t+1] = name + end + return t +end + +function lib:SetButtonRadius(radius) + if type(radius) == "number" then + lib.radius = radius + for _, button in next, lib.objects do + updatePosition(button, button.db and button.db.minimapPos or button.minimapPos) + end + end +end + +function lib:SetButtonToPosition(button, position) + updatePosition(lib.objects[button] or button, position) +end + +-- Upgrade! +for name, button in next, lib.objects do + local db = getDatabase(name) + if not db or not db.lock then + button:SetScript("OnDragStart", onDragStart) + button:SetScript("OnDragStop", onDragStop) + end + button:SetScript("OnEnter", onEnter) + button:SetScript("OnLeave", onLeave) + button:SetScript("OnClick", onClick) + button:SetScript("OnMouseDown", onMouseDown) + button:SetScript("OnMouseUp", onMouseUp) + + if not button.fadeOut then -- Upgrade to 39 + button.fadeOut = button:CreateAnimationGroup() + local animOut = button.fadeOut:CreateAnimation("Alpha") + animOut:SetOrder(1) + animOut:SetDuration(0.2) + animOut:SetFromAlpha(1) + animOut:SetToAlpha(0) + animOut:SetStartDelay(1) + button.fadeOut:SetToFinalAlpha(true) + end +end +lib:SetButtonRadius(lib.radius) -- Upgrade to 40 diff --git a/Libs/LibDBIcon-1.0/lib.xml b/Libs/LibDBIcon-1.0/lib.xml new file mode 100644 index 0000000..5dca17c --- /dev/null +++ b/Libs/LibDBIcon-1.0/lib.xml @@ -0,0 +1,7 @@ + + +